HDU3591 - The trouble of Xiaoqian(多重背包+完全背包)

【题意】题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3591 一个人需要购买价格为T元的商品,他身上带有面值为v1 v2 v3 v4 ...vn的货币分别有c1 c2 c3 c4...cn个,

售货员也有这些面值的货币,并且有无限个。问这人购买次商品最小需要和售货员流通的货币的个数是多少个?不超过20000个,超过就输出-1。流通的货币数目=购买人支付的货币数码+售货员找零的货币数目。

【分析】因为购买人支付的货币总价值(也就是支付的钱)可能超过商品价格T,所以把T的最大值10000看成背包容量,而把每个货币每次支付的个数看成价值。另外需要把购买人用多重背包算,因为他的货币数目不是无限的。dp[i][v]表示使用到第i种货币购买价格为v的物品需要支付的最少货币数目。

【AC代码】0ms

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 110
#define INF 0x3f3f3f3f
#define MAXC 20000 //最多给出的货币数目
#define MAXV 10000 //相当于背包容量
int w[MAXN],num[MAXN], dp[MAXC+1], dp2[MAXC+1];

int main ()
{
    #ifdef SHY
    freopen("e:\\1.txt", "r", stdin);
    #endif
    int count = 0, n, c;
    while(~scanf ("%d %d%*c", &n, &c) && n+c)
    {
        for(int i = 0; i < n; i++)
            scanf ("%d%*c", &w[i]);
        for (int i = 0; i < n; i++)
            scanf ("%d%*c", &num[i]);
        memset(dp,0x3f,sizeof(dp));
        dp[0] = 0;
        for (int i = 0; i < n; i++)//对购买人多重背包
        {
            if (num[i]*w[i] >= c)
            {
                for (int j = w[i]; j <= MAXV; j++)
                    dp[j] = min(dp[j],dp[j-w[i]]+1);
                continue;
            }
            for (int j = 1; j <= num[i]; num[i] -= j, j <<= 1)
            {
                int wg = j*w[i];
                for (int k = MAXV; k >= wg; k--)
                    dp[k] = min(dp[k],dp[k-wg]+j);//把每次支付的货币数目看成价值
            }
            if (num[i] > 0)
            {
                int wg = num[i]*w[i];
                for (int k = MAXV; k >= wg; k--)
                    dp[k] = min(dp[k],dp[k-wg]+num[i]);
            }
        }
        memset(dp2,0x3f,sizeof(dp2));
        dp2[0] = 0;
        for (int i = 0; i < n; i++)//对售货员进行完全背包
            for (int j = w[i]; j <= MAXV; j++)
                dp2[j] = min(dp2[j],dp2[j-w[i]]+1);
        int ans = INF;
        //找到最小的货币流通数目:min(购买人付的货币数目+售货员减去商品价格c的找零货币数目)
        for(int i = c; i <= MAXC; i++)
            ans = min(ans,dp[i]+dp2[i-c]);
        printf ("Case %d: ", ++count);
        if (INF == ans)
            puts("-1");
        else
            printf ("%d\n", ans);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值