完全背包
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 的情况下的最大价值

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号