题目描述
在之前的上机中,零崎已经出过了01背包和完全背包,也介绍了使用-1初始化容量限定背包必须装满这种小技巧,接下来的背包问题相对有些难度,可以说是01背包和完全背包的进阶问题。
多重背包:物品可以有0-n件。
对于第i种物品,我们有取0件,1件…n [ i ] 件共n [ i ] +1种策略,状态转移方程为f [ i ] [ v ] = max { f [ i - 1 ] [ v - k × c [ i ] ] + k × w [ i ] | 0 <=k<= n [ i ] }。在这里,很自然的有一种策略可以将其转化为01背包,即将物品换为n[i]件01背包中的物品,但是复杂度为O(VΣni),时间复杂度没有降低。实际上,对于所有类似情况,我们都可以利用二进制求和来降低时间复杂度。即将物品替换为价值和费用 * 系数=1,2,2^2,…,2^k,n[i]-2^k+1的物品。系数之和为n [ i ],表明不能取到多于n [ i ]件物品,但可以取到0…n[ i ]中任意一个整数件。利用这一优化,算法事件复杂度可以降到O(VΣlogni)。
实际上F [ i ] [ j ] 只依赖于 F [ i-1 ] [ j - k * w [ i ] ],这里依赖项之间构成了一个 { j mod w [ i ] }剩余类,不同剩余类之间无关,注意到这点利用单调队列,每个状态均摊O(1)的时间,可以进一步将算法时间复杂度优化至O(VN)级别的,不过在此不再详细阐述。(其实也就是NOIP程度,放在大学应该可以接受,但是这个优化个人感觉已经脱离dp)
DD大牛给出的伪代码。
def MultiplePack(F,C,W,M)
if C * M >= V
CompletePack(F,C,W)
return //考虑这里为什么可以直接用完全背包
k := 1
while k < M
ZeroOnePack(kC,kW)
M := M - k
k := 2k
ZeroOnePack(C M,W M)
输入
第一个数为数据组数n 1<=n<=10
接下来n组测试数据,每组测试数据由2部分组成。
第一行为背包容量V,物品种类数N。1<=V<=30000,1<=N<=200
接下来N行每行三个数为物品价值v,物品重量w,物品件数M。
1<=v,w<=200, 1<=M<=25
输出
对于每组数据,输出一行,背包能容纳的最大物品价值
输入样例
1
10 2
1 2 3
2 3 2
输出样例
6
解题思路:
题目来源:http://biancheng.love/contest/10/problem/E/index
问题属于背包问题,同时包括了0-1和完全背包,因此为多重背包问题。
按照之前的想法,只要判断每件物品的件数,可以确定对于该物品是使用0-1背包还是完全背包。
0-1背包的代码:
1 void Zeronepack(int w,int v) 2 { 3 for(int i=V; i>=w; i--) 4 if(dp[i]<dp[i-w]+v) 5 dp[i]=dp[i-w]+v; 6 }
完全背包的代码:
1 void Compack(int w,int v) 2 { 3 for(int i=w; i<=V; i++) 4 if(dp[i]<dp[i-w]+v) 5 dp[i]=dp[i-w]+v; 6 }
本题需要利用0-1背包以及完全背包来解决多重背包问题
代码:
1 #include <bits/stdc++.h> 2 #include<stdio.h> 3 #include<string.h> 4 int dp[30005]; 5 int V,N; 6 void Compack(int w,int v) 7 { 8 for(int i=w; i<=V; i++) 9 if(dp[i]<dp[i-w]+v) 10 dp[i]=dp[i-w]+v; 11 } 12 13 void Zeronepack(int w,int v) 14 { 15 for(int i=V; i>=w; i--) 16 if(dp[i]<dp[i-w]+v) 17 dp[i]=dp[i-w]+v; 18 } 19 20 int main() 21 { 22 int kase,v,w,m; 23 scanf("%d",&kase); 24 while(kase--) 25 { 26 memset(dp,0,sizeof(dp)); 27 scanf("%d%d",&V,&N); 28 for(int i=1; i<=N; i++) 29 { 30 scanf("%d%d%d",&v,&w,&m); 31 if(w*m>=V) 32 Compack(w,v); 33 else 34 { 35 for(int j=1; j<m; j<<1) 36 { 37 Zeronepack(j*w,j*v); 38 m-=j; 39 } 40 Zeronepack(m*w,m*v); 41 } 42 } 43 printf("%d ",dp[V]); 44 } 45 return 0; 46 }