动态规划解决0-1背包问题
字数 2152 2025-11-15 14:32:10
动态规划解决0-1背包问题
问题描述
0-1背包问题是一个经典的组合优化问题:给定一个容量为 \(C\) 的背包和 \(N\) 个物品,每个物品有重量 \(w_i\) 和价值 \(v_i\),要求选择若干物品放入背包,使得总重量不超过背包容量,且总价值最大。其中每个物品只能选择放入(1)或不放入(0),因此称为"0-1背包"。
核心难点
物品不可分割,需通过动态规划避免暴力枚举(时间复杂度为 \(O(2^N)\))。
步骤1:定义状态与子问题
- 状态定义:设 \(dp[i][c]\) 表示考虑前 \(i\) 个物品,在背包容量为 \(c\) 时能获得的最大价值。
- 子问题划分:将问题分解为逐步处理每个物品,并依赖更小容量的子问题结果。
步骤2:建立状态转移方程
对于第 \(i\) 个物品(重量 \(w_i\),价值 \(v_i\)),有两种选择:
- 不放入背包:最大价值与处理前 \(i-1\) 个物品时相同,即 \(dp[i][c] = dp[i-1][c]\)。
- 放入背包(需满足 \(c \geq w_i\)):价值为当前物品价值加上剩余容量 \(c-w_i\) 下的最大价值,即 \(dp[i][c] = dp[i-1][c-w_i] + v_i\)。
综合两种情况,状态转移方程为:
\[dp[i][c] = \begin{cases} \max(dp[i-1][c], dp[i-1][c-w_i] + v_i), & \text{if } c \geq w_i \\ dp[i-1][c], & \text{if } c < w_i \end{cases} \]
步骤3:初始化边界条件
- 容量为0时:任何物品都无法放入,价值恒为0,即 \(dp[i][0] = 0\)。
- 无物品时(\(i=0\)):无论容量多大,价值均为0,即 \(dp[0][c] = 0\)。
步骤4:确定计算顺序
- 使用双重循环:
- 外层循环遍历物品编号 \(i\) 从 1 到 \(N\)。
- 内层循环遍历背包容量 \(c\) 从 1 到 \(C\)。
- 确保计算 \(dp[i][c]\) 时,所需的 \(dp[i-1][c]\) 和 \(dp[i-1][c-w_i]\) 已被计算。
步骤5:举例演算
假设背包容量 \(C=5\),物品列表如下:
| 物品 | 重量 \(w_i\) | 价值 \(v_i\) |
|---|---|---|
| 1 | 2 | 6 |
| 2 | 1 | 3 |
| 3 | 4 | 8 |
| 4 | 3 | 7 |
构建 \(dp\) 表(行:物品编号,列:容量):
- 初始化:第0行和第0列全为0。
- 处理物品1(重量2,价值6):
- \(c=1\):容量不足,继承 \(dp[0][1]=0\)。
- \(c=2\):\(\max(dp[0][2], dp[0][0]+6) = \max(0, 6) = 6\)。
- \(c\geq2\) 时同理,值均为6。
- 处理物品2(重量1,价值3):
- \(c=1\):\(\max(dp[1][1], dp[1][0]+3) = \max(0, 3)=3\)。
- \(c=2\):\(\max(dp[1][2], dp[1][1]+3) = \max(6, 0+3)=6\)。
- \(c=3\):\(\max(6, dp[1][2]+3)=9\)。
- 继续计算完所有物品,最终 \(dp[4][5]=13\)(选择物品2、3、4)。
步骤6:空间优化(滚动数组)
- 观察状态转移方程:\(dp[i][c]\) 仅依赖于上一行 \(dp[i-1][\cdot]\)。
- 可优化为一维数组 \(dp[c]\),内层容量需倒序遍历(从 \(C\) 到 \(w_i\)),避免覆盖未使用的上一行数据。
- 转移方程简化为:
\[ dp[c] = \max(dp[c], dp[c-w_i] + v_i) \quad (\text{当 } c \geq w_i) \]
步骤7:复杂度分析
- 时间复杂度:\(O(N \times C)\)。
- 空间复杂度:未优化时为 \(O(N \times C)\),优化后为 \(O(C)\)。
总结
0-1背包问题的动态规划解法通过将问题分解为子问题,避免重复计算,核心在于状态定义、转移方程和计算顺序。空间优化技巧能显著降低内存使用,适用于容量较大的场景。