poj 3260 the fewest coins

那个给钱上限是百度的,原来我是用所有的价值加起来作为上线,结果TLE

背包问题都很简单,不过加上优化就可能难一点了

//f1[i]表示支付i元所需的最小硬币数 //f2[i]表示找i元所需的最小硬币数 //买T元东西所需的最小硬币数为f1[T + i] - f2[i] //f1[i] = min(f1[i], f1[i - k] + 1) //f2[i] = min(f2[i], f2[i - k] + 1) //其中f1[i]多重背包,用二进制拆分求解 //f2[i]为完全背包 //其中:给钱上界为:T+maxValue^2,其中maxValue为最大硬币面值。 //证明:反证法。假设存在一种支付方案,John给的钱超过T+maxValue^2, //则售货员找零超过maxValue^2,则找的硬币数目超过maxValue个,将其看作一数列, //求前n项和sum(n),根据鸽巢原理,至少有两 个对maxValue求模的值相等, //假设为sum(i)和sum(j),i<j,则i+1...j的硬币面值和为maxValue的倍数, //同理,John给的钱中也有 一定数量的硬币面值和为maxValue的倍数, //则这两堆硬币可用数量更少的maxValue面值硬币代替,产生更优方案。 #include <iostream> #include <cmath> #include <cstring> using namespace std; const int MAXN = 101; const int MAXT = 10000 + 120 * 120 + 1; const int INF = 200000000; int f1[MAXT]; int f2[MAXT]; int v[MAXN]; int c[MAXN]; int main() { //freopen("1.txt", "r", stdin); int n, T; while(cin >> n >> T) { int maxV = 0; for(int i = 0; i < n; i++) { cin >> v[i]; maxV = max(maxV, v[i]); } maxV *= maxV; int maxT = T + maxV; for(int i = 0; i < n; i++) cin >> c[i]; for(int i = 0; i <= maxT; i++) f1[i] = f2[i] = INF; f1[0] = f2[0] = 0; //完全背包求解 for(int i = 0; i < n; i++) { for(int j = v[i]; j < maxV; j++) { f2[j] = min(f2[j], f2[j - v[i]] + 1); } } //多重背包求解 for(int i = 0; i < n; i++) { int k = 1; int sum = 0; while(sum < c[i]) { for(int j = maxT; j >= v[i] * k; j--) { f1[j] = min(f1[j], f1[j - v[i] * k] + k); } sum += k; if(sum + k * 2 > c[i]) k = c[i] - sum; else k *= 2; } } int _min = INF; for(int i = T; i <= maxT; i++) { _min = min(_min, f1[i] + f2[i - T]); } if(_min == INF) printf("-1/n"); else printf("%d/n", _min); } return 0; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值