完全背包
-
N件物品
-
最多能背重量为W的背包
-
第i件物品的重量是weight[i]
-
第i件物品的价值是value[i]
-
每件物品无限个(可以放入背包多次)
求解将哪些物品装入背包里物品价值总和最大?
二维数组
-
确定dp数组下标含义
dp[i][j]
- 从下标为[0]到[i]的物品里取,每个物品可以取任意多个,放入容量为j的背包,此时的最大价值总和; -
确定递推公式
- 不放物品i:
dp[i][j] = dp[i - 1][j]
(物品重量大于背包容量,物品i放不进包里,背包价值不变; - 放物品i:
dp[i][j] = dp[i][j - weight[i]] + value[i]
(dp[i][j - weight[i]]
是容量为j - weight[i]时放过物品i的最大价值,dp[i][j - weight[i]] + value[i]
是此时放入物品i后背包的最大价值; - 综上:
dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
- 不放物品i:
-
初始化dp数组
-
容量j为0时,背包价值总和一定为0;
-
当j < weight[0]时,
dp[0][j] = 0
(背包容量比第0号物品小,放不进背包); -
当j > weight[0]时,
dp[0][j] = value[0]
(背包可以放下第0号物品);//参考代码如下,全部初始化为0则可省略第一个for循环 for (int j = 0; j < weight[0]; j++) dp[0][j] = 0; for (int j = weight[0]; j <= bagweight; j++) dp[0][j] = value[0];
-
-
确定遍历顺序
-
先遍历物品,后遍历背包
-
先遍历背包,后遍历物品
-
本质上:
dp[i][j]
由前序数据推出,for循环遍历次序不影响公式推导此处展示先外层遍历物品,嵌套内层遍历背包的代码:
for(int i = 1; i < weight.size(); i++) { // 遍历物品 for(int j = 0; j <= bagweight; j++) { // 遍历背包容量 if (j < weight[i]) dp[i][j] = dp[i - 1][j]; else dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]); } }
-
-
举例推导dp数组
滚动数组
所谓滚动数组,就是通过一维数组的方式压缩存储背包问题的状态。
-
确定dp数组下标含义
dp[i][j]
- 从下标为[0]到[i]的物品里取,每个物品可以取任意多个,放入容量为j的背包,此时的最大价值总和;dp[j]
- 容量为j的背包,每个物品可以无限量取,所装物品的最大价值总和;
-
确定dp数组递推公式
- 不放物品i:
dp[j] = dp[j]
; - 放物品i:
dp[j] = dp[j - weight[i]] + value[i]
; - 综上,递推式为
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
- 不放物品i:
-
初始化dp数组
dp[0] = 0;
- 其它下标可以初始化为0;
-
确定遍历顺序
-
正序遍历:每个物品i可被放入任意多次!
dp[i][j]
是由上一层dp[i - 1][ ]
计算而来,当前层的dp[i][j]
并不会被覆盖,顺序逆序遍历物品i都只被放入一次。如果顺序遍历dp[j]
数组,就相当于dp[i][j]
由层dp[i][ ]
计算而来,这个过程就重复放入物品i了。 -
先遍历物品,再嵌套遍历背包容量
正序遍历,如果先遍历背包容量,再嵌套物品,那么背包里都只放入了一个物品。
for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = weight[i]; j < bagWeight; j--) { // 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); } }
-
-
举例推导dp数组
完全背包与01背包的区别
不同点:
完全背包 | 01背包 | |
---|---|---|
约束条件 | 每个物品数量无限,可以放入任意多个; | 每个物品只有一个,只能放入一次; |
二维数组递推公式 | 放物品i:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]) | 放物品i:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) |
滚动数组遍历顺序 | 顺序遍历,只能外物品内嵌套背包 | 逆序遍历,for循环外物品或外背包均可(背包要求装满除外) |
相同点:
- 二维数组遍历时,for循环嵌套顺序不影响最终计算结果;
for嵌套顺序问题
-
二维数组:for循环嵌套先后顺序不影响最终计算结果(获得背包能背的最大价值)
-
滚动数组:
- 完全背包
- 一般情况下:嵌套顺序不影响最终计算结果(获得背包能背的最大价值)
- 背包要求装满:
- 外物品内背包:求排列数
- 外背包内物品:求组合数
- 01背包
- 外物品内背包:获得背包能背的最大价值
- 外背包内物品:背包里只放入了一个物品
- 完全背包
注:排列有序,组合无序。