完全背包
有 NN 种物品和一个容量是 VV 的背包,每种物品都有无限件可用。
第 ii 种物品的体积是 vivi,价值是 wiwi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 5 const int N=1010; 6 7 int dp[N][N]; 8 int w[N],v[N]; 9 int n,m; 10 11 int main() 12 { 13 cin>>n>>m; 14 15 for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; 16 17 //朴素版完全背包 18 for(int i=1;i<=n;i++) 19 for(int j=0;j<=m;j++) 20 for(int k=1;k*v[i]<=j;k++)//枚举第i个物品选了多少个 21 dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+w[i]*k); 22 23 /* 复杂度优化后完全背包 24 for(int i=1;i<=n;i++) 25 for(int j=0;j<=m;j++) 26 { 27 dp[i][j]=dp[i-1][j]; 28 if(j>=v[i]) 29 dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]); 30 }*/ 31 32 /* 降维优化完全背包 33 for(int i=1;i<=n;i++) 34 for(int j=v[i];j<=m;j++) 35 dp[j]=max(dp[j],dp[j-v[i]]+w[i]); 36 */ 37 printf("%d",dp[n][m]); 38 return 0; 39 }
优化思路
我们列举一下更新次序的内部关系:
f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....) f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-2*v]+2*w , .....) 由上两式,可得出如下递推关系: f[i][j]=max(f[i,j-v]+w , f[i-1][j])
由上两式,可得出如下递推关系:
f[i][j]=max(f[i,j-v]+w , f[i-1][j])
有了上面的关系,那么其实k循环可以不要了,核心代码优化成这样:
for(int i = 1 ; i <=n ;i++) for(int j = 0 ; j <=m ;j++) { f[i][j] = f[i-1][j]; if((j-v[i]>=0) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]); }
这个代码和01背包的非优化写法很像啊!!!我们对比一下,下面是01背包的核心代码
for(int i = 1 ; i <= n ; i++) for(int j = 0 ; j <= m ; j ++) { f[i][j] = f[i-1][j]; if(j-v[i]>=0) f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]); }
两个代码其实只有一句不同(注意下标)
f[ i ][ j ] = max(f[ i ][ j ],f[ i - 1 ][ j - v[ i ] ]+w[ i ]);//01背包
f[ i ][ j ] = max(f[ i ][ j ],f[ i ][ j - v[ i ] ]+w[ i ]);//完全背包问题
因为和01背包代码很相像,我们很容易想到进一步优化。核心代码可以改成下面这样
for(int i = 1 ; i<=n ;i++) for(int j = v[i] ; j<=m ;j++)//注意了,这里的j是从小到大枚举,和01背包不一样 { f[j] = max(f[j],f[j-v[i]]+w[i]); }