背包问题
简介
背包问题是一类动态规划问题的统称,分有多种子类型。
01背包
给定\(n\)个物品,每个物品都有自己的价值\(v_i\)和重量\(w_i\)。现有一个容量为\(W\)的背包,求最大价值。
很容易想到每种物品只有选或者不选,那么依次枚举即可。
考虑到还需要判断能否装下这些物品,所以还需要在转移的时候维护剩余容量。
因此设子状态\(f[i][j]\)为当前在第\(i\)个物品处,包括\(i\)在内已经选了重量为\(j\)的物品的最大价值。
状态转移方程为\(f[i][j]=max(f[i-1][j-v_i]+w[i],f[i-1][j])\)
此时的空间复杂度为\(O(NM)\),可以通过滚动数组优化到\(O(M)\)。
事实上,还可以进行进一步优化:
由于每一层\(i\)之间是互相独立的(也就是说\(f[i][j]\)不会被\(f[i][k]\)更新),所以我们可以优化掉第一维,但是此时需要修改第二维的枚举顺序。
根据状态转移方程可知,第二维大的状态是由第二维小的状态转移来的,由于我们优化掉了第一维,所以必须先遍历第二维大的状态,否则将会出现覆盖的情况。
所以第一维遍历仍然顺序,第二维遍历须改为逆序。
完全背包
条件和01背包一致,但是每种物品都有无限个。
子状态与上一题一致,但是转移过程有不同。
我们将上一题中第二维的遍历顺序改为顺序即可解决完全背包问题。
改为顺序后,即会出现同层之间互相转移的情况,也就是实现了每个物品可以无限选取的题设。
多重背包
条件与01背包一致,但是每种物品都有\(c_i\)个。
朴素的思路是将每种物品拆开,转换为01背包进行求解,时间复杂度为\(O(m\sum c_i)\)。
更为优秀的思路是将每种物品个数量按照二进制拆开。
对于物品\(i\),把它拆成重量为\(2^0*v_i,2^1*v_i,...,2^p*v[i],r_i*v_i\)的物品,显然\([1*v_i,c_i*v_i]\)中的所有重量都能被组合出来。
这样的时间复杂度为\(O(mlogc_i)\)。
分组背包
有\(n\)组,每组都有\(m\)个物品,每组最多选一个,求最大价值。
子状态\(f[i][j]\)表示选到了第\(i\)组,总重量为\(j\)的最大价值。
\(f[i][j]=max(f[i-1][j],max(f[i-1][j-v_{ik}]+w_{ik}))\)
可以像01背包一样优化掉第一维。