多重背包

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

接下来是二进制优化版本,未优化版本与完全背包问题基本一致

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 const int N=30000;
 6 
 7 int w[N],v[N],dp[N];
 8 int n,m;
 9 
10 int main()
11 {
12     cin>>n>>m;
13     
14     int cnt=0;
15     while(n--)
16     {
17         int a,b,s;
18         cin>>a>>b>>s;
19         
20         int k=1;
21         while(k<=s)
22         {
23             cnt++;
24             v[cnt]=a*k;
25             w[cnt]=b*k;
26             s-=k;
27             k*=2;
28         }
29         if(s>0)
30         {
31             cnt++;
32             v[cnt]=a*s;
33             w[cnt]=b*s;
34         }
35     }
36     
37     for(int i=1;i<=cnt;i++)
38         for(int j=m;j>=v[i];j--)
39             dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
40     
41     printf("%d",dp[m]);
42     return 0;
43 }

二进制优化基本思想就是从1开始以2的倍数倍增,并将这些集合的物品打包形成一个新的物品,由此可以转化为01背包问题(其中任意的集合组合可以表示成小于s[i]的任意一个数)

思路和多重背包问题I一样,但这题的数据范围变成1000了,非优化写法时间复杂度O(n^3) 接近 1e9

必超时。

优化多重背包的优化
首先,我们不能用完全背包的优化思路来优化这个问题,因为每组的物品的个数都不一样,是不能像之前一样推导不优化递推关系的。

我们列举一下更新次序的内部关系:

 dp[i , j ] = max( dp[i-1,j] , dp[i-1,j-v]+w , dp[i-1,j-2*v]+2*w , dp[i-1,j-3*v]+3*w , .....) 
dp[i , j-v]= max( dp[i-1,j-v] , dp[i-1,j-2*v] + w , dp[i-1,j-2*v]+2*w , .....)

 

由上两式,可得出如下递推关系: dp[ i ][ j ]=max(dp[ i , j - v ]+w , dp[ i - 1 ][ j ])

接下来,我介绍一个二进制优化的方法,假设有一组商品,一共有11个。我们知道,十进制数字 11 可以这样表示
11=1011(B)=0111(B)+(11−0111(B))=0111(B)+0100(B)
11=1011(B)=0111(B)+(11−0111(B))=0111(B)+0100(B)

正常背包的思路下,我们要求出含这组商品的最优解,我们要枚举12次(枚举装0,1,2....12个)。

现在,如果我们把这11个商品分别打包成含商品个数为1个,2个,4个,4个(分别对应0001,0010,0100,0100)的四个”新的商品 “, 将问题转化为01背包问题,对于每个商品,我们都只枚举一次,那么我们只需要枚举四次 ,就可以找出这含组商品的最优解。 这样就大大减少了枚举次数。

这种优化对于大数尤其明显,例如有1024个商品,在正常情况下要枚举1025次 , 二进制思想下转化成01背包只需要枚举10次。

优化的合理性的证明
先讲结论:上面的1,2,4,4是可以通过组合来表示出0~11中任何一个数的,还是拿11证明一下(举例一下):

首先,11可以这样分成两个二进制数的组合:
11=0111(B)+(11−0111(B))=0111(B)+0100(B)
11=0111(B)+(11−0111(B))=0111(B)+0100(B)

其中0111通过枚举这三个1的取或不取(也就是对0001(B),0010(B),0100(B)的组合),可以表示十进制数0~7( 刚好对应了 1,2,4 可以组合出 0~7 ) , 0~7的枚举再组合上0100(B)( 即 十进制的 4 ) ,可以表示十进制数 0~11。其它情况也可以这样证明。这也是为什么,这个完全背包问题可以等效转化为01背包问题。

posted @ 2020-11-10 02:19  筱翼深凉  阅读(173)  评论(0编辑  收藏  举报