一、题目
有N种物品和一个容量为V 的背包
第i种物品最多有 Mi 件可用,每件耗费的空间是Ci,价值是W
求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大
二、基本算法
这题目和完全背包问题很类似
基本的方程只需将完全背包问题的方程略微一改即可
因为对于第 i 种物品有 Mi+1 种策略:
取0件,取1件……取Mi件
令 dp[i, v]表示前 i 种物品恰放入一个容量为v的背包的最大价值,则有状态转移方程:
dp[i, v] = max{dp[i ? 1, v ? k ? Ci] + k ? Wi | 0 ≤ k ≤ Mi}
复杂度是O(V * ΣMi)
3、转化为01背包问题
另一种好想好写的基本方法是转化为01背包求解:
把第i种物品换成 Mi 件01背包中的物品,则得到了物品数为 ΣMi 的01背包问题
直接求解之,复杂度仍然是O(V * ΣMi)
但是我们期望将它转化为01背包问题之后,能够像完全背包一样降低复杂度
仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品
使得原问题中第i种物品可取的每种策略——取 0...Mi 件——均能等价于取若干件代换以后的物品
另外,取超过Mi件的策略必不能出现
方法是:
将第 i 种物品分成若干件01背包中的物品,其中每件物品有一个系数
这件物品的费用和价值均是原来的费用和价值乘以这个系数
令这些系数分别为1, 2, 2^2 ... 2^(k?1), Mi ? 2^k + 1,且 k 是满足 Mi ? 2^k + 1 > 0 的最大整数
例如,如果Mi为13,则相应的k = 3,这种最多取13件的物品应被分成系数分别为1, 2, 4, 6的四件物品
分成的这几件物品的系数和为 Mi,表明不可能取多于 Mi 件的第i种物品
另外这种方法也能保证对于0...Mi间的每一个整数,均可以用若干个系数的和表示
这里算法正确性的证明可以分0... 2k?1和2k ...Mi两段来分别讨论得出
这样就将第i种物品分成了 O(logMi) 种物品,将原问题转化为了复杂度为 O(V ΣlogMi) 的01背包问题,是很大的改进
下面给出O(logM)时间处理一件多重背包中物品的过程:
def MultiplePack(dp, C, W ,M)
if C · M ≥ V
CompletePack(dp, C, W )
return
k := 1
while k < M
ZeroOnePack(kC, kW)
M := M ? k
k := 2k
ZeroOnePack(C · M, W · M)
四、O(V * N)算法
多重背包问题同样有O(V N)复杂度的算法
这个算法基于基本算法的状态转移方程
但应用单调队列的方法使每个状态的值可以以均摊O(1)的时间求解
这个方法在楼天成的“男人八题”幻灯片上
单调队列的讲解:
五、小结
在这一讲中,我们看到了将一个算法的复杂度由O(V ΣMi)改进到O(V ΣlogMi)的过程
还知道了存在复杂度为O(V N)的算法
要特别注意“拆分物品”的思想和方法,自己证明一下它的正确性,并将完整的程序代码写出来
转自《背包九讲V_2.0》%%%作者,感谢作者