两类简单背包问题总结

一、01 背包问题

  1. 01 背包问题描述:有 n 件物品,每件的重量为 w[i] ,价值为 c[i] 。现有一个容量为 V 的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每件物品都只有 1 件。
  2. 暴力解法的时间复杂度为O\left ( 2^{n} \right ),利用动态规划时间复杂度可降为O\left ( nV \right )
  3. 令 dp[i][v] 表示前 i 件物品(1 <= i <= n, 0 <= v <= V)恰好装入容量为 v 的背包中所能获得的最大价值。在求解 dp[i][v] 时,考虑第 i 件物品的选择,有两种策略:①不放第 i 件物品,那么问题转化为前 i-1 件物品恰好装入容量为 v 的背包中所能获得的最大价值,也即 dp[i-1][v]。②放第 i 件物品,那么问题转化为前 i-1 件物品恰好装入容量为 v-w[i] 的背包中所能获得的最大价值,也即 dp[i-1][v-w[i]]+c[i]。由于只有这两种策略,且要求获得最大价值,因此dp[i][v]=max\left \{ dp[i-1][v],dp[i-1][v-w[i]]+c[i] \right \}\left ( 1\leqslant i\leqslant n,w[i]\leqslant v\leqslant V \right ),此即为状态转移方程。边界为dp[0][v]=0\left ( 0\leqslant v\leqslant V \right )。因此可以写出代码如下。
  4. for (int i = 1; i <= n; i++)
        for (int v = w[i]; v <= V; v++)
            dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - w[i]] + c[i]);
  5. 可以知道时间复杂度和空间复杂度都是 O\left ( nV \right ),其中时间复杂度已无法再优化,但是空间复杂度还可以再优化。
  6. 注意到状态转移方程中计算 dp[i][v] 时需要计算 dp[i-1][0]~dp[i-1][v-w[i]] 中的最大值,而计算 dp[i+1][v] 时又需要计算 dp[i][0]~dp[i][v-w[i]] 中的最大值,dp[i-1][] 部分的数据又完全用不到了,因此可以直接开一个一维数组 dp[v],枚举方向为 i 从 1 到 n,v 从 V 到 0(逆序),这样状态转移方程改变为dp[v]=max\left ( dp[v],dp[v-w[i]]+c[i] \right )\left ( 1\leqslant i\leqslant n,w[i]\leqslant v\leqslant V \right ),边界为dp[v]=0\left ( 0\leqslant v\leqslant V \right )。空间复杂度降为O\left ( V \right ),代码如下。
  7. for (int i = 1; i <= n; i++)
    	for (int v = V; v >= w[i]; v--)
    		dp[v] = max(dp[v], dp[v - w[i]] + c[i]);
  8. 一般对能够划分阶段的问题,都可以尝试把阶段作为状态的一维,这可以使我们更方便地得到满足无后效性的状态。如果当前设计的状态不满足无后效性,那么不妨把状态升维,即增加一维或若干维来表示相应的信息,这样可能就能满足无后效性了。

二、完全背包问题

  1. 完全背包问题描述:有 n 件物品,每件的重量为 w[i] ,价值为 c[i] 。现有一个容量为 V 的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品都有无穷件。
  2. 和 01 背包一样,完全背包问题的每种物品都有两种策略,但是也有不同点。对第 i 件物品来说:①不放第 i 件物品,那么 dp[i][v] =dp[i-1][v],这跟 01 背包是一样的;②放第 i 件物品,这里的处理和 01 背包有所不同。完全背包选择放第 i 件物品之后并不是转移到 dp[i-1][v-w[i],而是转移到 dp[i][v-w[i]],这是因为每种物品可以放任意件,放了第 i 件物品后还可以继续放第 i 件物品,直到第 v-w[i] 小于 0 为止。因此状态转移方程为:dp[i][v]=max\left \{ dp[i-1][v],dp[i][v-w[i]]+c[i] \right \}\left ( 1\leqslant i\leqslant n,w[i]\leqslant v\leqslant V \right ),边界为dp[0][v]=0\left ( 0\leqslant v\leqslant V \right )
  3. 同样也可以改写成一维形式的状态转移方程:dp[v]=max\left ( dp[v],dp[v-w[i]]+c[i] \right )\left ( 1\leqslant i\leqslant n,w[i]\leqslant v\leqslant V \right ),边界为dp[v]=0\left ( 0\leqslant v\leqslant V \right )。写成一维形式之后和 01 背包完全相同,唯一的区别在于这里 v 的枚举顺序是正向枚举,而 01 背包的一维形式中 v 必须是逆向枚举。完全背包的一维形式代码如下。
  4. for (int i = 1; i <= n; i++)
    	for (int v = w[i]; v <= V; v++)
    		dp[v] = max(dp[v], dp[v - w[i]] + c[i]);

三、参考文献

[1] 胡凡,曾磊.算法笔记[M].机械工业出版社,2016:465.

猜你喜欢

转载自blog.csdn.net/weixin_42717165/article/details/87900245