背包问题关系图
问题描述
若有 N 件物品和一个最多能装重量为 W 的背包,一个物品只有两个属性:重量和价值。第i件物品的重量是weight[i],得到的价值是value[i] 。假设每件物品只能用一次,将哪些物品装入背包里物品价值总和最大?
注意:0-1 背包问题无法使用贪心算法来求解,也就是说,不能按照先添加性价比最高的物品来达到最优,因为这种方式可能造成背包空间的浪费,从而无法达到最优。
基本思路
上述问题是典型的动态规划,这里有两个可变量:重量和价值,我们定义dp[i][j]:前i件物品重量不超过j时背包能达到的最大价值
,背包的初始状态是最终目标是dp[N][W]
,即前N件物品重量不超过W时背包能达到的最大价值。
设第 i 件物品重量为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论:
- 第 i 件物品没添加到背包,最大价值:
dp[i][j] = dp[i - 1][j]
。 - 第 i 件物品添加到背包中:
dp[i][j] = dp[i - 1][j - w] + v
。
第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w] + v)
#include<bits/stdc++.h> using namespace std; int dp[30][30005]; int n,m; int w[30],v[30];//w重量,v价值 int main() { cin>>m>>n; for(int i=1;i<=n;i++) { int x,y; cin>>x>>y; w[i] = x; //重量 = 价钱 v[i] = x*y; //价值 = 价钱x*重要度y } //以下是01背包环节 for(int i=1;i<=n;i++) //先循环物品 { for(int j=1;j<=m;j++) //再循环容量 { if(j-w[i]>=0) dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]); else //不能取第i个物品时的最大值与上一层i-1相同 dp[i][j] = dp[i-1][j]; } } cout<<dp[n][m]; return 0; }
优化
由于dp[i][j]
的值只与dp[i-1][0,...,j-1]
有关,可以采用动态规划常用的优化方法对空间进行优化(即去掉dp的第一维)。因此,0-1 背包的状态转移方程为: dp[j] = max(dp[j], dp[j - w] + v)
特别注意:为了防止上一层循环的dp[0,...,j-1]
被覆盖,循环的时候 j 只能逆向遍历。优化空间复杂度:
#include<bits/stdc++.h> using namespace std; int dp[30005]; //优化后dp数组为最大背包容量 int n,m; int w[30],v[30];//w重量,v价值 int main() { cin>>m>>n; for(int i=1;i<=n;i++) { int x,y; cin>>x>>y; w[i] = x; //重量 = 价钱 v[i] = x*y; //价值 = 价钱x*重要度y } //以下是01背包环节 for(int i=1;i<=n;i++) //先循环物品 { for(int j=m;j>=1;j--) //再循环容量,一维背包为防止覆盖先前数据要逆序循环 { dp[j] = max(dp[j],dp[j-w[i]]+v[i]); //一维背包的状态转移方程 } } cout<<dp[m]; return 0; }01背包内循环理解:还原成二维的dp就很好理解,一维的dp是二维dp在空间上进行复用的结果。
dp[i]=f(dp[i-num])
,等式的右边其实是二维dp上一行的数据,应该是只读的,在被读取前不应该被修改。如果正序的话,靠后的元素在读取前右边的dp有可能被修改了,倒序可以避免读取前被修改的问题。
标签:11,900,01,int,件物品,重量,背包,价值,dp From: https://www.cnblogs.com/jyssh/p/16918060.html