逆序

一、一维背包问题中的逆序与顺序

1. 0-1背包问题(逆序更新)

问题描述

  • 背包容量为 C,物品体积为 w[i],价值为 v[i]
  • 每个物品只能选一次,求最大价值。

关键点

  • 逆序遍历容量(从 Cw[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背包问题 使用未污染的历史状态(旧值)
顺序更新 完全背包问题 使用可能被污染的最新状态(新值)
  • 逆序更新保护了历史状态,确保每个物品只被选一次。
  • 顺序更新允许状态被多次覆盖,从而实现重复选择。

通过调整遍历顺序,可以灵活控制物品的选择策略,这是动态规划在背包问题中的精髓。

posted @ 2025-02-26 13:26  咋还没来  阅读(43)  评论(0)    收藏  举报