用填充表格法吃透01背包及其变形-2

2.3 步骤3:dp数组如何初始化

初始化逻辑与二维一致:容量为0时,最大价值为0,因此dp[0] = 0;其他容量的初始值也为0(因为初始无物品可放,最大价值为0),即dp = new Array(capacity + 1).fill(0)

初始化后的单行表格:0,0,0,0,0,0,0,0,0(j从0到8)

2.4 步骤4:确定遍历顺序(表格填充顺序)

一维DP的遍历顺序有严格要求,核心是「倒序遍历容量」,对应单行表格的「从右往左填充」------明确单行表格的填充顺序是避免重复选择物品的关键:

  1. 必须先遍历物品,再遍历容量:逐个处理每个物品,每次处理时更新整个单行表格(覆盖上一行结果);

  2. 容量必须倒序遍历(j从C到weightsi-1 :从最大容量往小容量填充,确保计算dp[j]时,dp[j - weights[i-1]]仍是上一行(未处理当前物品)的旧值,避免同一物品被多次选择。

关键原因:一维DP的核心是用单行表格复用二维表格的空间,表格中每个位置的数值都依赖"上一轮未更新的旧值"(对应二维的dp[i-1][j - w[i]])。若正序遍历容量,dp[j - w[i]]会被提前更新(相当于二维的dp[i][j - w[i]]),导致同一个物品被多次选择(变成完全背包);倒序遍历能保证计算dp[j]时,dp[j - w[i]]仍是上一行(未选当前物品)的结果,对应单行表格从右往左填充,完美契合01背包「每个物品选一次」的规则。

反例(正序遍历容量):若j从weightsi-1到C正序遍历,处理物品1(w=2,v=3)时,j=2会更新dp2=3,j=4时会用到dp2的新值(3),计算dp4 = dp4 + 3 = 3,相当于把物品1放入了两次,违背01背包规则。

2.5 步骤5:打印dp数组(验证)

通过打印单行表格的滚动更新过程,验证填充规则的正确性。仍用测试用例 weights = [2,3,4,5]values = [3,4,5,6]capacity = 8,演示一维DP数组(单行表格)的填充变化:

  1. 初始状态:dp = 0,0,0,0,0,0,0,0,0

  2. 处理物品1(w=2,v=3),j从8到2倒序

    更新后:dp = 0,0,3,3,3,3,3,3,3

    • j=8:dp8 = max(0, dp8-2+3) = max(0,0+3)=3;

    • j=7:dp7 = max(0, dp5+3)=3;

    • ...(j=2到6同理);

    • j=2:dp2 = max(0, dp0+3)=3;

  3. 处理物品2(w=3,v=4),j从8到3倒序

    更新后:dp = 0,0,3,4,4,7,7,7,7

    • j=8:max(3, dp5+4)=max(3,3+4)=7;

    • j=7:max(3, dp4+4)=max(3,3+4)=7;

    • j=6:max(3, dp3+4)=max(3,3+4)=7;

    • j=5:max(3, dp2+4)=max(3,3+4)=7;

    • j=4:max(3, dp1+4)=max(3,0+4)=4;

    • j=3:max(3, dp0+4)=max(3,0+4)=4;

  4. 处理物品3(w=4,v=5),j从8到4倒序

    更新后:dp = 0,0,3,4,5,5,8,9,9

    • j=8:max(7, dp4+5)=max(7,4+5)=9;

    • j=7:max(7, dp3+5)=max(7,4+5)=9;

    • j=6:max(7, dp2+5)=max(7,3+5)=8;

    • j=5:max(7, dp1+5)=max(7,0+5)=7;

    • j=4:max(4, dp0+5)=max(4,0+5)=5;

  5. 处理物品4(w=5,v=6),j从8到5倒序

    更新后:dp = 0,0,3,4,5,6,8,9,10

    • j=8:max(9, dp3+6)=max(9,4+6)=10;

    • j=7:max(9, dp2+6)=max(9,3+6)=9;

    • j=6:max(8, dp1+6)=max(8,0+6)=8;

    • j=5:max(5, dp0+6)=max(5,0+6)=6;

最终单行表格dp[8] = 10,与二维DP结果一致,验证了优化的正确性。

2.6 一维DP空间优化完整代码(JavaScript)

javascript 复制代码
/**
 * 基础01背包(一维DP空间优化解法)
 * @param {number[]} weights - 物品重量数组
 * @param {number[]} values - 物品价值数组
 * @param {number} capacity - 背包最大容量
 * @returns {number} - 背包能容纳的最大价值
 */
function knapsack_1d(weights, values, capacity) {
  const n = weights.length;
  // 1. 初始化一维dp数组:dp[j]表示容量j的背包的最大价值,初始值0
  const dp = new Array(capacity + 1).fill(0);

  // 2. 遍历顺序:先遍历物品(i从0到n-1),再倒序遍历容量(j从capacity到weights[i])(从右往左填充)
  for (let i = 0; i < n; i++) {
    // 倒序遍历避免重复选择当前物品
    for (let j = capacity; j >= weights[i]; j--) {
      // 3. 递推公式:不选当前物品的最大价值 vs 选当前物品的最大价值
      dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
    }
    // 打印每次处理物品后的dp数组(单行表格更新过程)
    console.log(`处理完物品${i + 1}后,dp数组:`, [...dp]);
  }

  // 最终答案:容量为capacity的背包的最大价值
  return dp[capacity];
}

// 测试用例
const weights1 = [2, 3, 4, 5];
const values1 = [3, 4, 5, 6];
const capacity1 = 8;
console.log('最大价值:', knapsack_1d(weights1, values1, capacity1)); // 输出:10
相关推荐
GetcharZp10 分钟前
GitHub 49K+ Star!C++ 开发者必知的 JSON 神级库:从零到精通全指北
后端
问心无愧051316 分钟前
ctf show web入门111
android·前端·笔记
xujinwei_gingko18 分钟前
SpringBoot整合WebSocket
spring boot·后端·websocket
唐某人丶25 分钟前
模型越来越强,我们还需要 Agent 工程吗?—— 从价值重估到 Harness 实践
前端·agent·ai编程
智码看视界37 分钟前
现代Web开发基础:全栈工程师的起航点
前端·后端·c5全栈
程序员cxuan39 分钟前
Claude Fable 5 来了
人工智能·后端·程序员
小欣加油1 小时前
leetcode56 合并区间
c++·算法·leetcode·职场和发展
JS菌1 小时前
手写一个 AI Agent 全栈项目:从沙箱执行到子智能体的完整实现
前端·人工智能·后端
lqqjuly1 小时前
前沿算法深度解析(二)
人工智能·算法·机器学习