完全背包

1. 完全背包

现有 \(n\) 种物品, 每种物品都有很多个(无穷), 已知第 \(i\) 种物品的体积为 \(v_i\), 价值为 \(w_i\), 问: 一个容量为 \(m\) 的背包所能装走这些物品的最大价值为多少

1.1 状态表示

设状态 \(f_{i, j}\) 表示前 \(i\) 种物品, 在体积不超过 \(j\) 的情况下的最大价值

1.2 状态转移方程

1.2.1 基础状态转移方程

\(f_{i, j} = \max\limits_{k = 0}^{+\infty}(f_{i - 1, j - k \times v_i} + k \times w_i)\)

1.2.2 优化的状态转移方程

若是按照上面那个状态转移方程写代码的话, 时间复杂度会达到 \(O(nm^2)\)
然而我们发现了一个规律: \(f_{i, j - v_i} = \max\limits_{k = 1}^{+\infty}(f_{i - 1, j - k \times v_i} + (k - 1) \times w_i)\)
\(f_{i, j} = \max\limits_{k = 0}^{+\infty}(f_{i - 1, j - k \times v_i} + k \times w_i)\) \(= \max(f_{i - 1, j}, \max\limits_{k = 1}^{+\infty}(f_{i - 1, j - k \times v_i} + k \times w_i))\) \(= \max(f_{i - 1, j}, \max\limits_{k = 1}^{+\infty}(f_{i - 1, j - k \times v_i} + (k - 1) \times w_i) + w_i)\) \(= \max(f_{i - 1, j}, f_{i, j - v_i} + w_i)\)

即: \(f_{i, j} = \max(f_{i - 1, j}, f_{i, j - v_i} + w_i)\)
这样一优化, 时间复杂度就和 01背包 一样, 为 \(O(nm)\)

1.3 代码实现

由于更新 \(f_{i, j}\) 时, 所需要的 \(f_{i, j - v_i}\) 也要在这之前更新, 所以当使用滚动数组的时候, 第二层循环不用像 01背包 那样逆序

for (int i = 1; i <= n; ++ i)
  for (int j = v[i]; j <= m; ++ j)
    f[j] = max(f[j], f[j - v[i]] + w[i]);

cout << f[m] << endl; // f[m] 表示前 n 种物品, 在体积不超过 m 的情况下的最大价值
posted @ 2021-10-30 16:15  小阁下  阅读(55)  评论(0)    收藏  举报