01给定 个物品,第 个物品的重量为 、价值为 ,和一个容量为 的背包。每个物品只能选择一次,问在限定背包容量下能放入物品的最大价值。

对于每个物品来说,不放入背包,背包容量不变;放入背包,背包容量减小。由此可得状态定义:当前物品编号 i 和背包容量 c ,记为 [i,c] 。

状态 [i,c] 对应的子问题为:前 i 个物品在容量为 c 的背包中的最大价值,记为 dp[i,c] 。

待求解的是 dp[n,cap] ,因此需要一个尺寸为 (n+1)×(cap+1) 的二维 dp 表。

当我们做出物品 i 的决策后,剩余的是前 i−1 个物品决策的子问题,可分为以下两种情况。

  • 不放入物品 i :背包容量不变,状态变化为 [i−1,c] 。
  • 放入物品 i :背包容量减少 wgt[i−1] ,价值增加 val[i−1] ,状态变化为 [i−1,c−wgt[i−1]] 。

本题的最优子结构:最大价值 dp[i,c] 等于不放入物品 i 和放入物品 i 两种方案中价值更大的那一个。由此可推导出状态转移方程:

dp[i,c]=max(dp[i−1,c],dp[i−1,c−wgt[i−1]]+val[i−1])

需要注意的是,若当前物品重量 wgt[i−1] 超出剩余背包容量 c ,则只能选择不放入背包。 Pasted image 20250701223448

Pasted image 20250701224046

动态规划

/* 0-1 背包:动态规划 */
function knapsackDP(wgt, val, cap) {
    const n = wgt.length;
    // 初始化 dp 表
    const dp = Array(n + 1)
        .fill(0)
        .map(() => Array(cap + 1).fill(0));
    // 状态转移
    for (let i = 1; i <= n; i++) {
        for (let c = 1; c <= cap; c++) {
            if (wgt[i - 1] > c) {
                // 若超过背包容量,则不选物品 i
                dp[i][c] = dp[i - 1][c];
            } else {
                // 不选和选物品 i 这两种方案的较大值
                dp[i][c] = Math.max(
                    dp[i - 1][c],
                    dp[i - 1][c - wgt[i - 1]] + val[i - 1]
                );
            }
        }
    }
    return dp[n][cap];
}
 

reference

https://www.hello-algo.com/chapter_dynamic_programming/knapsack_problem/