有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。
第 ii 件物品的体积是 vivi,价值是 wiwi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,VN,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤10000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
第一种动态规划二维版本:
思路:
闫式DP分析法:
划分为两部分:
第一部分:状态表示 f[i][j]:
(又分为两步--->)
(1)集合:所有的选法和选取条件---->本题只从i个物品中选取,和物品总体积小于等于j
(2)属性分为--->最大值,最小值(这个二维有时候需要考虑边界问题,初始化个正无穷就好),数量
第二部分是状态计算------>集合的划分:
本题难点
分不含i和含i----->不含i用f[i - 1][j],含i用f[i - 1,(j - v[i]) + w[i]]表示
1 #include2 #include 3 4 using namespace std; 5 6 const int N = 1010; 7 8 int n,m; 9 int v[N],w[N]; 10 int f[N][N]; 11 int main(){ 12 cin >> n >> m; 13 for(int i = 1;i <= n;i++) cin >> v[i] >> w[i]; 14 15 //物品的数量和背包体积 16 for(int i = 1;i <= n;i++) 17 for(int j = 0;j <= m;j++) 18 { 19 //不含第i个物品,和含有第i个物品,不含第i个物品一定存在,但是含有第i个物品不一定存在 20 //因为我们加上第i个物品的体积可能会大于我们的背包体积(j < vi),所以右边可能会是空集 21 //第i-1个个物品一定存在,先存到i里边 22 f[i][j] = f[i - 1][j]; 23 //在原有i-1这个物品的数量体积上,在进行判断是否能在装有第i-1物品的体积基础上用我们的第i个物品的体积进行替换 24 //使我们背包的利用率达到最大,也就是进行判断在数量为i的物品背包容量不超过j的情况下,能装的物品的最大价值 25 //是多少 26 //背包体积不超过j的情况下,用上一个价值去和当前价值进行比较 27 //f[i][j] 表示的就是我们背包当前的状态表示 28 if(j >= v[i]) f[i][j] = max(f[i][j],f[i - 1][j - v[i]] + w[i]); 29 } 30 cout << f[n][m]; 31 32 33 }
接下来是很秀的转化为一维的操作:
由转状态转移方程----->
不含i用f[i - 1][j],含i用f[i - 1,(j - v[i]) + w[i]]
第一个优化:
我们的f[i]只用到了f[i - 1]这一层,f[i-2],f[i-3]....等层数都没有被用到,所以可以用滚动数组来做。
第二个优化:
不管我们用到的哪个j都是<=j的,没有在j的两侧,所以就可以改为一维数组来算
具体改法:
1 //转化 2 #include3 #include 4 5 using namespace std; 6 7 const int N = 1010; 8 9 int n,m; 10 int v[N],w[N]; 11 int f[N]; 12 int main(){ 13 cin >> n >> m; 14 for(int i = 1;i <= n;i++) cin >> v[i] >> w[i]; 15 16 //物品的数量和背包体积 17 for(int i = 1;i <= n;i++) 18 for(int j = m;j >= v[i];j--) 19 { 20 //对代码做等价变形去掉f[i][j] = f[i-1][j] 21 //j从0 ~ v[i-1]没有意义,所以说j从v[i]开始就可以了 22 //由于j >= j - v[i] 又因为j 是从小到大枚举 23 //所以说f[j - v[i]]在第i层被计算过了(也就是说原来的i-1就是现在的i,因为从小到大所以小的就已经被算过了) 24 //所以说f[j - v[i]]其实是第二层的j - v[i],也就是等价于原先二维的 25 //max(f[i][j],f[i][j - v[i]]);这样就和原来的f[i - 1][j - v[i]]不一样了 26 27 //解决问题的方案:只需要把j的循环变成从大到小就可以了 28 //这样的话我们去算f[j]的时候,由于j - v[i] <= j,又因为我们从大到小枚举所有的体积 29 //所以我们在算f[j]的时候,f[j - v[i]]还没有被更新过,那么他存地就是第i - 1层的v[i] 30 //那么就等价于f[i - 1][j - v[i]],,,,这样就对了 31 f[j] = max(f[j],f[j - v[i]] + w[i]); 32 } 33 cout << f[m]; 34 }