完全背包

完全背包的基本模型和01背包的差不多,只是从每件物品只有一个换成了每件物品有无限多个。

首先我们就想到了可以把它转化为01背包的问题来做啊,我们只需要把每一类物品拆分成v/volume[i]个物品,然后把它们当成01背包的问题来解决就行了,这是显而易见的思路,那就看看代码怎么实现吧,以杭电1114为例:

HDU-1114 PiggyBank

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1114

Piggy-Bank

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 17717    Accepted Submission(s): 8935


Problem Description
Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid. 

But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs! 
 

Input
The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it's weight in grams. 
 

Output
Print exactly one line of output for each test case. The line must contain the sentence "The minimum amount of money in the piggy-bank is X." where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line "This is impossible.". 
 

Sample Input
   
   
   
   
3 10 110 2 1 1 30 50 10 110 2 1 1 50 30 1 6 2 10 3 20 4
 

Sample Output
   
   
   
   
The minimum amount of money in the piggy-bank is 60. The minimum amount of money in the piggy-bank is 100. This is impossible.
 
题意大家可以自行理解,下面是我将其转化为01背包解法的代码。

#include<iostream>
#include<algorithm>
#include<math.h>
#define maxn 10005
#define dada 1000000
using namespace std;
int dp[maxn],value,weight;
int    main(){
    int t;
    cin>>t;
    while(t--){
        dp[0]=0;
        for(int i=1;i<maxn;i++)
            dp[i]=dada;
        int e,f;
        cin>>e>>f;
        int c=f-e;
        int n;
        cin>>n;
        int num=0;
        for(int i=1;i<=n;i++){
            cin>>value>>weight;
            num=c/weight;
            for(int j=1;j<=num;j++){
                for(int k=c;k>=0;k--)
                    if(k>=weight)
                        dp[k]=min(dp[k],dp[k-weight]+value);
            }
        }
        if(dp[c]==dada){
            cout<<"This is impossible."<<endl;
        }
        else
            cout<<"The minimum amount of money in the piggy-bank is "<<dp[c]<<"."<<endl;
    }
    return 0;
}

这个代码交上去结果是意料之中的TLE,但是本身我们将其转化为01背包的方法和思路事没有问题的,但是这种做法的弊端也是显而易见的,时间复杂度很高,数据稍微大一点就会超时。对于这种解法本身,如果不转换思路的话,时间复杂度上倒是可以利用转换为二进制优化一下,可以得到明显改善,这个优化的原理和方法我会在另一篇博客中专门解释这个,此处不再展开。

我们还有另外一种时间复杂度更为优化的方案来解决完全背包问题,简单到出人意料:只需要将第二层循环从01背包解法中强调的逆序变为正序,那么最后输出的就是完全背包的解了。我刚看到是也是完全不信的,但是事实说明这是真的。。。。先上代码,仍旧是这道题。

#include<iostream>
#include<algorithm>
#include<math.h>
#define maxn 10005
#define dada 1000000
using namespace std;
int dp[maxn],value,weight;
int    main(){
    int t;
    cin>>t;
    while(t--){
        dp[0]=0;
        for(int i=1;i<maxn;i++)
            dp[i]=dada;
        int e,f;
        cin>>e>>f;
        int c=f-e;
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>value>>weight;
            for(int j=0;j<=c;j++){
                    if(j>=weight)
                        dp[j]=min(dp[j],dp[j-weight]+value);
            }
        }
        if(dp[c]==dada){
            cout<<"This is impossible."<<endl;
        }
        else
            cout<<"The minimum amount of money in the piggy-bank is "<<dp[c]<<"."<<endl;
    }
    return 0;
}

然后就AC了。。。然后就没有然后了。

本来这样就行了,但是为什么呢?为什么你一倒过来就完全背包了呢?代码你也得讲道理啊,就是你是代码你也不能耍流氓啊怎么换个顺序就这样了呢?当然这是有原因的。

我们看这里最核心的状态转移方程:

 dp[j]=min(dp[j],dp[j-weight]+value);
我们的原理是问每一件物品取了好还是不取好,在01背包中最核心的就是只能逆序以避免我们用这件物品已经取过的值覆盖掉了上一行的值,而如果我们正序呢?那就变成了每一次都用的是本行的值,也就是已经取过这个物品了结果在同一件物品遍历的过程中一直再问取不取取不取。结果当然就是我们每件物品可以取任意个了,而且还是最优解。

想通之后只能痛心疾首地说,你赢了,我服了你。

你可能感兴趣的:(题解,杭电,背包问题,完全背包详解)