HDU 3591 (完全背包+二进制优化的多重背包)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=3591

The trouble of Xiaoqian

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2798    Accepted Submission(s): 972


Problem Description
In the country of ALPC , Xiaoqian is a very famous mathematician. She is immersed in calculate, and she want to use the minimum number of coins in every shopping. (The numbers of the shopping include the coins she gave the store and the store backed to her.)
And now , Xiaoqian wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, ..., VN (1 ≤ Vi ≤ 120). Xiaoqian is carrying C1 coins of value V1, C2 coins of value V2, ...., and CN coins of value VN (0 ≤ Ci ≤ 10,000). The shopkeeper has an unlimited supply of all the coins, and always makes change in the most efficient manner .But Xiaoqian is a low-pitched girl , she wouldn’t like giving out more than 20000 once.
 
Input
There are several test cases in the input.
Line 1: Two space-separated integers: N and T.
Line 2: N space-separated integers, respectively V1, V2, ..., VN coins (V1, ...VN)
Line 3: N space-separated integers, respectively C1, C2, ..., CN
The end of the input is a double 0.
 
Output
Output one line for each test case like this ”Case X: Y” : X presents the Xth test case and Y presents the minimum number of coins . If it is impossible to pay and receive exact change, output -1.
 
Sample Input
3 70
5 25 50
5 2 1
0 0
 
Sample Output
Case 1: 3
 
Author
alpc97
 分析:
第一次做两种背包结合在一起的问题
写了差不多3小时,到处看看大佬的解题思路,终于弄懂了
题意:
 
有一个具有n种货币的货币系统, 每种货币的面值为val[i]. 现在小杰手上拿着num[1],num[2],…num[n]个第1种,第2种…第n种货币去买价值为T(T<=20000)的商品, 他给售货员总价值>=T的货币,然后售货员(可能,如果小杰给的钱>T,那肯定找钱)找钱给他. 售货员每次总是用最少的硬币去找钱给小杰. 现在的问题是: 小杰买价值T的商品时, 他给售货员的硬币数目+售货员找他的硬币数目最少等于多少?
 
第一行先输入n和c,n代表硬币的种类数,c代表顾客需要付的款数,
第二行是顾客n种硬币的面值
第三行是顾客相应的n种硬币的个数
 
顾客和店员的区别:
具有的硬币的种类数都是n种,不过顾客的硬币数是有限的(输入的相应硬币数目),店员的硬币数目是无限的
 
现在顾客需要买c元的东西,只能用这些硬币付款,且顾客手中不同种类的硬币的数目是有限的,所以现在顾客付款有两种情况:
1.店员不需要找钱 顾客手中的硬币种类和个数刚好可以组成c元
2.店员需要找钱,顾客手中硬币种类和个数不可以恰好组成c元,所以顾客只能给T元,店员给顾客找钱c-T元
现在要 求经过店员和顾客手中的硬币的最小数目
 
解题思路;
主要的方向就是对店员进行完全背包从操作,对顾客进行多重背包的操作,然后最后遍历两个dp数组,找到最小硬币数目(t=min(t,dp2[i]+dp1[i-c]))
很多需要注意的地方:
1.dp数组初始化
因为是要能付款,而不是求硬币数目最少,所以是"恰恰好装满背包“
所以两个数组除了dp[0]以外,都要赋无穷大
2.多重背包要采用二进制优化,不然会超时
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define max_v 20010
#define INF 0x3f3f3f3f
int w[max_v],num[max_v];
int dp1[max_v],dp2[max_v];
int main()
{
    int n,c,yy=1;
    while(~scanf("%d %d",&n,&c))
    {
        if(n==0&&c==0)
            break;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&w[i]);
        }
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&num[i]);
            sum=sum+num[i];
        }
        //初始化
        for(int i=0;i<max_v;i++)
        {
            dp1[i]=dp2[i]=INF;//无穷max
        }
        dp1[0]=0;
        dp2[0]=0;
        //对店员完全背包
        for(int i=0;i<n;i++)
        {
            for(int j=w[i];j<=20000;j++)
            {
                dp1[j]=min(dp1[j],dp1[j-w[i]]+1);
            }
        }
        //对顾客多重背包 二进制优化
        for(int i = 0;i < n;i++)
        {
            for(int k = 1;k <= num[i];k*=2)
            {
                for(int j = max_v - 1;j >= k*w[i];j--)
                dp2[j] = min(dp2[j] , dp2[j-k*w[i]] + k) ;
                num[i]-=k;
            }
            for(int j = max_v;j >= num[i]*w[i];j--)
            dp2[j] = min(dp2[j] , dp2[j - num[i]*w[i]] + num[i]) ;
        }

        int t=INF;
        for(int i=c;i<=20000;i++)
        {
            t=min(t,dp2[i]+dp1[i-c]);
        }
        if(t==INF)
        {
            t=-1;
        }
        printf("Case %d: %d\n",yy,t);
        yy++;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/yinbiao/p/9097765.html