前言
动态规划专题,从简到难通关动态规划。
什么是完全背包
完全背包是一种经典的动态规划问题,它是背包问题的一种变形。在完全背包问题中,有一个容量为C的背包和n种物品,第i种物品有一个重量为wi和一个价值为vi。每种物品可以无限次地选择放入背包中,问背包能够容纳的最大价值是多少?
01背包和完全背包问题都是经典的动态规划问题,它们都是背包问题的变体。它们的主要区别在于每种物品选择次数上的限制不同。
假设背包最⼤重量为4。
| 物品编号 | 重量 | 价值 |
|---|---|---|
| 物品0 | 1 | 15 |
| 物品1 | 3 | 20 |
| 物品2 | 4 | 30 |
01背包的核心递推公式为:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
在01背包中,对于一个物品只有选择或者不选择,所以可以根据这两个状态推断出选择方式,
在完全背包中,每种物品可以选择放入背包的次数不受限制,因此将物品1放入背包中的次数增加了,从而得到了不同于01背包问题的解。在此问题中,放入物品0、1、2可以得到背包最大价值为60。
对于完全背包来说,我们就可以去计算每一个物品的价值期望,比如说物品0的价值期望为 15/1 等于15,而物品1的价值期望为 20/3, 物品2为 30/4
那么我们还是按照动态规划五部曲来解决一下这个入门问题
- 定义dp数组
dp[i][j]表示当背包容量为j,前i个物品可以装入背包时的最大总价值。
- 推导递推公式
在状态转移时,我们需要考虑两种情况:
(1)当前不将第i个物品装入背包中,则dp[i][j] = dp[i-1][j]。
(2)当前将第i个物品装入背包中,则dp[i][j] = dp[i][j-weight[i]] + value[i];
考虑上述两种情况的转移方程,可以列出如下的状态转移方程:
if (j >= weight[i - 1]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]);
} else {
dp[i][j] = dp[i - 1][j];
}
- 初始化数组
dp数组中初始值都是0,因为当背包容量为0或者不装入物品时,背包的总价值必须为0。
- 确定遍历顺序
在遍历dp数组时,i表示物品可以选择的数量,j表示背包容量,我们需要先枚举物品,再枚举背包容量。这样才能保证在求解物品i时,之前物品的状态已经全部被更新保存在dp数组中。
function testCompletePack(bagWeight, weight, value) {
let dp = new Array(weight.length + 1).fill().map(() => new Array(bagWeight + 1).fill(0));
for (let i = 1; i <= weight.length; i++) {
for (let j = 1; j <= bagWeight; j++) {
if (j >= weight[i - 1]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
console.log(dp[weight.length][bagWeight]);
}
testCompletePack(4, [1, 3, 4], [15, 20, 30]); // 60