动态规划常用框架
定义dp数组,确定其含义;
初始化dp数组;
求解递推公式
一、0-1背包
问题描述
有n种物品, 每种物品只有一个,放入容量为w的背包中,求可以放入的最大价值
二维数组解题
原理
- dp[i][j]数组表示在0到i件商品中任取商品放入容量为j的背包中最大价值
- 需要初始化二维数组dp第一行和第一列
- 递推公式的确定(由两部分组成)
不放物品i:背包中的价值和dp[i - 1][j]
放物品i: dp[i - 1][j - weight[i]] + value[i] (物品i的价值)
故, dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) - 遍历顺序可以调换(可以先遍历物品,再遍历容量;反过来也可以),找个例子手推一遍就会发现,遍历顺序的调换不影响递推公式的确定(遍历顺序的调换其实改变的是二维数组的填充顺序)
JS代码
/**
* 二维数组
* @param {Array} weight 物品重量数组
* @param {Array} value 物品价值数组
* @param {Number} size 背包容量
*/
function testWeightBagProblem(weight, value, size) {
// 物品的个数
let len = weight.length
let dp = Array(len)
.fill()
.map((item) => Array(size + 1).fill(0))
// 初始化第一行
for (let j = weight[0]; j < size; j++) {
dp[0][j] = value[0]
}
for (let i = 1; i < len; i++) {
for (let j = 1; j <= size; j++) {
if (j < weight[i]) {
dp[i][j] = dp[i - 1][j]
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
}
}
}
return dp[len - 1][size]
}
console.log(testWeightBagProblem([1, 3, 4], [15, 20, 30], 4))
一维数组解题
原理
- 就是将二维数组进行压缩,外层遍历物品,内层遍历容量(必须使用倒序),这个遍历顺序是不能变的
- 内层遍历容量使用倒序的原因
二维数组解题时,计算dp[i][j]用的是dp[i-1]那一行的元素;而一维数组解题,计算dp[j]用的是本行元素,如果正序遍历,前面的数据改了,后面的数据再计算时就会出错(每个物品可能会放入背包多次),但倒序遍历就会避免这个问题 - 遍历每一个物品,一维数组都会重用,然后在此基础上修改一维数组;找个例子手推一遍就会发现把每个物品的一维数组合并就是之前的二维数组
- dp[j]表示容量为j的背包中可以放入的最大价值
- 递推公式
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]) - 将dp[j]初始化为0,这样不会影响通过递推公式求解dp[j]
JS代码
/**
* 一维数组
* @param {Array} weight 物品重量数组
* @param {Array} value 物品价值数组
* @param {Number} size 背包容量
*/
function testWeightBagProblem2(weight, value, size) {
// 初始化dp数组
let dp = Array(size + 1).fill(0)
// 外层遍历物品,内层遍历容量(倒序,保证每一个物品只会放入一次),遍历顺序不可变
for (let i = 0; i < value.length; i++) {
for (let j = size; j >= weight[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
}
}
return dp[size]
}
二、完全背包
问题描述
有n种物品, 每种物品有无限个,放入容量为w的背包中,求可以放入的最大价值
一维数组解题
原理
- 和0-1背包的一维数组解题一样,只不过在遍历容量的时候是正序遍历
- 遍历物品和遍历容量的顺序可以调换
JS代码
function testWeightBagProblem(weight, value, size) {
let dp = Array(size + 1).fill(0)
for (let i = 0; i < value.length; i++) {
for (let j = weight[i]; j <= size; j++) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
}
}
return dp[size]
}
console.log(testWeightBagProblem([1, 3, 4], [15, 20, 30], 4))