poj 3260 The Fewest Coins (完全背包+多重背包)

【题意】:John要去买价值为m的商品. 现在的货币系统有n种货币,对应面值为val[1],val[2]…val[n]. 然后他身上每种货币有num[i]个. John必须付给售货员>=m的金钱, 然后售货员会用最少的货币数量找钱给John.
问你John的交易过程中, 他给售货员的货币数目+售货员找钱给他的货币数目 的和最小值是多少?

【思路】:dp1表示支付i元所需的最少硬币,dp2表示找零i元所需的最少硬币
dp1是多重背包,dp2是完全背包

所以递推公式为:dp2[j] = min(dp2[j], dp2[j - v[i]] + 1);
网上还有一篇关于付钱金额上界的文章,不是很懂,有兴趣的可以点这里

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int t, n;
int v[maxn], c[maxn];
int dp1[10005], dp2[10005];
/*dp1表示支付i元所需的最少硬币,dp2表示找零i元所需的最少硬币
dp1是多重背包,dp2是完全背包
所以结果应该是dp1[i + t] + dp2[i],则会就是付款t元所需的最少硬币

*/
void zero(int w, int x, int sum) {
    for(int j = sum; j >= w; j--) {
        dp1[j] = min(dp1[j], dp1[j - w] + x);
        //因为是二进制优化的,将k个一样的物品看成一个新物品,所以要加上x而不是1(x就是kFF
    }
}
int main() {
    scanf("%d%d", &n, &t);
    memset(dp1, inf, sizeof(dp1));
    memset(dp2, inf, sizeof(dp2));
    dp1[0] = 0;
    dp2[0] = 0;
    int sum = 0;
    for(int i = 1; i <= n; i++) scanf("%d", &v[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
    for(int i = 1; i <= n; i++) sum += v[i] * c[i];//确定一个最大值
    for(int i = 1; i <= n; i++) {
        for(int j = v[i]; j <= sum; j++)
            dp2[j] = min(dp2[j], dp2[j - v[i]] + 1);
    }
    for(int i = 1; i <= n; i++) {
        if(c[i]*v[i] >= t) {
            for(int j = v[i]; j <= sum; j++)
                dp1[j] = min(dp1[j], dp1[j - v[i]] + 1);
        }   else {
            int k = 1;
            int num = c[i];
            while(k < num) {
                zero(k * v[i], k, sum);
                num -= k;
                k *= 2;
            }
            zero(num * v[i], num, sum);
        }
    }
    int ans = inf;
    for(int i = 0; i <= sum - t; i++) {
        ans = min(ans, dp1[i + t] + dp2[i]);
    }
    if(ans < inf)printf("%d\n", ans);
    else printf("-1\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80479616