Consumer [分组背包]

Consumer

**Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/65536 K (Java/Others)
Total Submission(s): 2657 Accepted Submission(s): 1397
**

Problem Description

FJ is going to do some shopping, and before that, he needs some boxes to carry the different kinds of stuff he is going to buy. Each box is assigned to carry some specific kinds of stuff (that is to say, if he is going to buy one of these stuff, he has to buy the box beforehand). Each kind of stuff has its own value. Now FJ only has an amount of W dollars for shopping, he intends to get the highest value with the money.

Input

The first line will contain two integers, n (the number of boxes 1 <= n <= 50), w (the amount of money FJ has, 1 <= w <= 100000) Then n lines follow. Each line contains the following number pi (the price of the ith box 1<=pi<=1000), mi (1<=mi<=10 the number goods ith box can carry), and mi pairs of numbers, the price cj (1<=cj<=100), the value vj(1<=vj<=1000000)

Output

For each test case, output the maximum value FJ can get

Sample Input

3 800
300 2 30 50 25 80
600 1 50 130
400 3 40 70 30 40 35 60

Sample Output

210

题解

这是一道有依赖的分组背包的裸题

下面是背包九讲里面的一段话

考虑到所有这些策略都是互斥的(也就是说,你只能选择一种策略),所以一个主件和它的附件集合实际上对应于 P06 中的一个物品组,每个选择了主件又选择了若干个附件的策略对应于这个物品组中的一个物品,其费用和价值都是这个策略中的物品的值的和。但仅仅是这一步转化并不能给出一个好的算法,因为物品组中的物品还是像原问题的策略一样多。
再考虑 P06 中的一句话:可以对每组中的物品应用 P02 中“一个简单有效的优化”。这提示我们,对于一个物品组中的物品,所有费用相同的物品只留一个价值最大的,不影响结果。所以,我们可以对主件 i 的“附件集合”先进行一次 01 背包,得到费用依次为 0..V-c[i] 所有这些值时相应的最大价值 f’[0..V-c[i]] 。那么这个主件及它的附件集合相当于 V-c[i]+1 个物品的物品组,其中费用为 c[i]+k 的物品的价值为 f’[k]+w[i] 。也就是说原来指数级的策略中有很多策略都是冗余的,通过一次 01 背包后,将主件 i 转化为 V-c[i]+1 个物品的物品组,就可以直接应用 P06 的算法解决问题了。

设置一个dp数组,dp[i][j]表示到第i个箱子,用了j的钱获得的最大价值

根据这一段话,因为箱子里的物品不会再是箱子也就是箱子不会嵌套,我们可以把一个箱子及里面的物品看成一个物品组.

如果选这个箱子那么我们还可以选择选不选或者选几个里面的物品,而显然,花费价格相同的情况下,我们只需要保留一个最大价值,这并不会影响结果的正确性(一个小贪心),那么我们就可以在选了这个箱子的情况下(一定是只针对这个箱子),对里面的物品做一次01背包

在求出最大值之后我们还要和之前的箱子去比,相同情况下我们选择价值更大的箱子
因为每做一次,我们就比对一次,所以前一个一定是最大的,最后答案就保存在dp[n][v]里


      for(int j=0;j<=V;j++) dp[i][j]=max(dp[i][j],dp[i-1][j]);

在处理单个箱子时,我们判断一下它的价格,即小于箱子价格的 都不能买,全部赋值成一个很小的数,注意是一个很小的数,不是-1或是0这样的,因为要保证它对后面的转移没有影响,即加上在多也依然是一个很小的数,取max一定取不到它
而大于等于它的就赋值成原来的价值,因为买箱子是没有价值的


      for(int j=0;j<p;j++) dp[i][j]=-inf;
      for(int j=p;j<=V;j++) dp[i][j]=dp[i-1][j-p];

具体AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#define MIN(a,b) (a)>(b)?(b):(a)
#define MAX(a,b) (a)>(b)?(a):(b)
#define in(i) (i=read())
using namespace std;
int read(){
    int ans=0,f=1;
    char i=getchar();
    while(i<'0'||i>'9'){
        if(i=='-') f=-1;
        i=getchar();
    }
    while(i>='0' && i<='9'){
        ans=(ans<<1)+(ans<<3)+i-'0';
        i=getchar();
    }
    return ans*f;
}
const int inf=2000000;
int n,V;
int w[11],v[11];
int dp[51][100010];
int main()
{
    while(cin>>n>>V){
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            int p,m;
            in(p);in(m);
            for(int j=1;j<=m;j++){
                in(w[j]);in(v[j]);
            }
            for(int j=0;j<p;j++) dp[i][j]=-inf;
            for(int j=p;j<=V;j++) dp[i][j]=dp[i-1][j-p];
            for(int j=1;j<=m;j++)
                for(int k=V;k>=w[j];k--)
                    dp[i][k]=max(dp[i][k],dp[i][k-w[j]]+v[j]);
            for(int j=0;j<=V;j++) dp[i][j]=max(dp[i][j],dp[i-1][j]);
        }
        cout<<dp[n][V]<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/real-l/p/9183483.html