2. 01背包问题

有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vivi,价值是 wiwi。

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

输入格式

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

接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。

输出格式

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

数据范围

0<N,V10000<N,V≤1000
0<vi,wi10000<vi,wi≤1000

输入样例

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 #include<iostream>
 2 #include<algorithm>
 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 #include<iostream>
 3 #include<algorithm>
 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 }







原文地址:https://www.cnblogs.com/luyuan-chen/p/11615687.html