题解 P2851 【[USACO06DEC]The Fewest Coins G】
题
官方题解中提到找零数的上界是
这里给出了找零数
首先一个显然的思路,枚举找零的量
看到这里有一个问题,这个
直观感受下,这个
实际上,如果面值最大的币面值为
- 首先,找零中面值非最大的币总共最多用
v_{max} 个。
反证,如果
接下来分类讨论。
这里先说明一下,下面的过程都是按在付钱部分和找零部分中不断同时删去一些面值之和相等的硬币,直到按该方法无法再删去任何硬币为止分析的。
- 找零中有面值最大的币
显然,此时付钱中不能有面值最大的币了,否则可以两边各删一个硬币,直到付钱中没有或者找零中没有为止。
所以付钱中所有的钱都不是面值最大的币,那么如果付钱中硬币的数量小于
找零中面值最大的币少于
- 找零中没有面值最大的币
很明显,找零小于
所以,在
代码:
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL V = 28800; // 2 * v_{max}^2
LL n,t;
LL v[105],c[105];
LL dp1[V << 1],dp2[V << 1]; // 这里我直接 2V 了,其实 V+t 就够
LL ans = INF;
int main(){
memset(dp1,INF,sizeof(dp1));
memset(dp2,INF,sizeof(dp2));
cin >> n >> t;
for(LL i = 1;i <= n;i ++) cin >> v[i];
for(LL i = 1;i <= n;i ++) cin >> c[i];
dp1[0] = dp2[0] = 0;
for(LL i = 1;i <= n;i ++)
for(LL j = v[i];j <= V;j ++) dp2[j] = min(dp2[j],dp2[j - v[i]] + 1);
for(LL i = 1;i <= n;i ++){
for(LL w = 1;w <= c[i];w <<= 1){
for(LL j = V + t;j >= v[i] * w;j --) dp1[j] = min(dp1[j],dp1[j - v[i] * w] + w);
c[i] -= w;
}
if(c[i]) for(LL j = V + t;j >= v[i] * c[i];j --) dp1[j] = min(dp1[j],dp1[j - v[i] * c[i]] + c[i]);
}
for(LL i = t;i <= t + V;i ++) ans = min(ans,dp1[i] + dp2[i - t]);
if(ans >= INF) ans = -1;
cout << ans << '\n';
return 0;
}