背包九讲-第二讲 完全背包

题目

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

基本思路

01背包不同的是每种物品都有无限件,对每种物品的策略也不是取或不取,而是取0件 取1件 取2件······

如果按照01背包的思路,仍然可以得到类似的状态转移方程
f[i][v] = max(f[i-1][v-k*c[i]] + k*w[i] | 0<=k*c[i]<=v)

一个简单有效的优化

如果两件物品满足

  1. c[i]<=c[j]
  2. w[i]>=w[j]
    则可以将物品j去掉,不用考虑.

这个简单的优化可以用O(n^2)实现,虽然并不能改善最坏情况的复杂度,但对于随机生成的数据,这个方法往往能大大减少物品的件数.

另外一个优化就是,先去掉体积大于背包容量的物品,然后找到相同体积中价值最高的物品. 这个优化可以在O(V+N)时间复杂度内完成.

转化为01背包求解

上一讲里我们学会了01背包的解法,如果我们能把完全背包转化为01背包就可以求解,一种简单的想法是:
假设第i种物品的体积是c[i],背包容量是V,则第i种物品最多装V/c[i]件,则我们可以假设有V/c[i]件i物品,则问题转化为01背包

一种更高效的转化方法是: 我们没必要将物品完全分解为V/c[i]件,根据二进制的思想,我们可以把第i种物品拆分成体积为c[i]*2^k 价值为w[i]*2^k的若干件物品

更高效的O(VN)算法

考虑基本思路中的状态转移方程f[i][v] = max(f[i-1][v-k*c[i]] + k*w[i] | 0<=k*c[i]<=v)

可以等价的变形为
f[i][v] = max(f[i-1][v], f[i][v-c[i]]+w[i])
参考第一讲01背包的思路,使用一维数组实现上述转移方程

伪代码

for i=1...N
    for v=0...v
        f[v]=max(f[v], f[v-c[i]]+w[i])

可以发现和01背包的区别只是v的循环顺序不同,在01背包中要按照v=V...0的逆序来循环是因为要保证第i次循环的状态是由f[i-1][v-c[i]]递推而来. 而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v=0..V的顺序循环。这就是这个简单的程序为何成立的道理。

值得一提的是,上面的伪代码中两层for循环的次序可以颠倒。这个结论有可能会带来算法时间常数上的优化。

posted @ 2017-08-26 20:13  lepeCoder  阅读(243)  评论(0编辑  收藏  举报