06.0-1背包问题理论基础
- 题目描述
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
- 题目分析
1. 背包问题是一类典型的动态规划题目,它是想在有限的背包容量中
装入不同重量,不同价值的物品,使得最终在没有超过背包容量的
前提下,使得背包的价值取得最大值。
2. 0-1背包问题是指:在每个物品均只有1个的情况下进行选择,每个
物品可放可不放,存在这样的两种状态。
动态规划五部曲
1. 确定dp数组以及下标的含义
设置二维数组dp[i][j]:表示选取物品编号为0-i的物品,书包容量为j的情况下,背包的最大价值
dp[i][j]
i:物品0~物品i j:背包的容量为j
容量0 容量1 容量2 容量3 容量4
编号0
编号0-1
编号0-2
编号0-3
2. 确定递推公式
已知物品的重量为weight[i],物品的价值为value[i]
不放物品i的最大重量为:dp[i-1][j]
放置物品i的最大重量为:dp[i-1][j-weight[i]] + value[i]
则在背包容量为j的情况下,背包的最大价值为:
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
3. dp数组如何初始化
当j=0时,由于背包容量为0,所以此列的最大价值均为0
当i=0是,背包只能装物品0,当背包容量>=物品0重量时可以初始化为物品0的价值
4. 确定遍历顺序
这里选择先遍历物品,再遍历背包
5. 举例推导dp数组
- Java代码关键步骤
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 - 1][j - weight[i]] + value[i]);
}
}
- 0-1背包问题降阶处理
根据上述讨论已经得到了0-1背包问题的递推公式
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
不难看出dp[i][j]取决于dp[i-1][j],因此可以将二维数组压缩成一位dp数组,dp[j]表示背包容量为j时的最大价值
压缩后的递推公式为
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
Java核心代码
for (int i = 1; i < items.length; i++) { // 遍历物品
for (int j = badweight; j > 0; j--) { // 遍历背包容量
if (j >= item[i]) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
/*
else dp[j] = dp[j];//可以省略
*/
}
在解决背包问题时,内层循环倒序遍历背包容量是为了避免重复计算。
背包问题具有重叠子问题的性质,即通过不同的选择可能会得到相同的状态。在这种情况下,如果按顺序遍历背包容量,会在计算某个状态时用到尚未更新的状态,从而导致结果不正确。
倒序遍历背包容量可以保证在计算某个状态时,所有可能需要用到的子问题状态都已经计算完成。这样可以避免重复计算,确保得到的结果是正确的。
总而言之,dp[j]的状态取决于前面的状态,若从前往后遍历会导致大量重复,进而使得结果错误