逆序
一、一维背包问题中的逆序与顺序
1. 0-1背包问题(逆序更新)
问题描述:
- 背包容量为
C,物品体积为w[i],价值为v[i]。 - 每个物品只能选一次,求最大价值。
关键点:
- 逆序遍历容量(从
C到w[i])保证每个物品只被选一次。
代码示例:
int[] dp = new int[C + 1];
for (int i = 0; i < n; i++) {
for (int j = C; j >= w[i]; j--) { // 逆序更新
dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
}
}
原理分析:
- 假设当前处理物品
i,容量j从高到低遍历。 - 当计算
dp[j]时,dp[j - w[i]]是尚未被本次物品更新的旧值(即未包含当前物品i的状态)。 - 这样确保每个物品只被选一次。
2. 完全背包问题(顺序更新)
问题描述:
- 物品可以无限次选择。
关键点:
- 顺序遍历容量(从
w[i]到C)允许重复选择。
代码示例:
for (int i = 0; i < n; i++) {
for (int j = w[i]; j <= C; j++) { // 顺序更新
dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
}
}
原理分析:
- 当计算
dp[j]时,dp[j - w[i]]可能已经被本次物品更新过。 - 这意味着可以重复选择当前物品
i。
二、二维背包问题中的逆序更新
问题描述:
- 背包有两个限制:体积
m和重量q。 - 物品有体积
s[i]、重量w[i]和价值v[i]。 - 每个物品只能选一次。
关键点:
- 双重逆序更新:同时对体积和重量两个维度逆序遍历。
代码示例:
int[][] dp = new int[q + 1][m + 1];
for (int k = 0; k < n; k++) { // 遍历物品
for (int i = q; i >= s[k]; i--) { // 逆序体积
for (int j = m; j >= w[k]; j--) { // 逆序重量
dp[i][j] = Math.max(dp[i][j], dp[i - s[k]][j - w[k]] + v[k]);
}
}
}
原理分析:
- 逆序遍历确保在更新
dp[i][j]时,dp[i - s[k]][j - w[k]]是未包含当前物品k的旧值。 - 如果顺序更新,
dp[i - s[k]][j - w[k]]可能已经被当前物品污染,导致重复选择。
三、具体例子演示
例1:一维背包问题
假设物品参数:w[0] = 2, v[0] = 5,背包容量 C = 4。
逆序更新过程:
初始状态: [0, 0, 0, 0, 0]
处理物品0(体积2,价值5):
j=4: dp[4] = max(0, dp[4-2]+5) = max(0, 0+5) = 5
j=3: dp[3] = max(0, dp[1]+5) = 0
j=2: dp[2] = max(0, dp[0]+5) = 5
最终状态: [0, 0, 5, 0, 5]
- 物品0被选一次,最大价值为5。
顺序更新过程:
初始状态: [0, 0, 0, 0, 0]
处理物品0(体积2,价值5):
j=2: dp[2] = max(0, dp[0]+5) = 5
j=3: dp[3] = max(0, dp[1]+5) = 0
j=4: dp[4] = max(0, dp[2]+5) = 10
最终状态: [0, 0, 5, 0, 10]
- 物品0被重复选择两次,价值错误地达到10。
例2:二维背包问题
假设物品参数:s[0] = 2(体积),w[0] = 1(重量),v[0] = 5,背包限制 m=3(体积)、q=2(重量)。
逆序更新过程:
初始状态(q=2, m=3): dp[2][3] = 0
处理物品0:
i从2到2(体积限制),j从3到1(重量限制):
dp[2][3] = max(0, dp[0][2] + 5) = 5
最终状态: dp[2][3] = 5
- 物品0被正确选一次。
顺序更新过程:
假设顺序更新 i=2→2, j=1→3:
第一次 j=1: dp[2][1] = 0 → 无变化
j=2: dp[2][2] = max(0, dp[0][1]+5) = 5
j=3: dp[2][3] = max(0, dp[0][2]+5) = 5
此时,再次处理物品0时可能重复选择(但代码中物品循环在外层,顺序更新会导致错误)。
四、核心总结
| 更新方式 | 适用场景 | 数据依赖关系 | 是否允许重复选择 |
|---|---|---|---|
| 逆序更新 | 0-1背包问题 | 使用未污染的历史状态(旧值) | 否 |
| 顺序更新 | 完全背包问题 | 使用可能被污染的最新状态(新值) | 是 |
- 逆序更新保护了历史状态,确保每个物品只被选一次。
- 顺序更新允许状态被多次覆盖,从而实现重复选择。
通过调整遍历顺序,可以灵活控制物品的选择策略,这是动态规划在背包问题中的精髓。

浙公网安备 33010602011771号