一、01背包
(1)问题描述:有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是,得到的
价值是 。求解将哪些物品装入背包可使价值总和最大。
F[i,v]表示前i件物品放入一个容量为v的背包可以获得的最大价值,状态转移方程:
(2)伪代码:
F [0, 0..V ] ← 0
for i ← 1 to N
for v ← Ci to V
F [i, v] ← max{F [i − 1, v], F [i − 1, v − Ci] + Wi}
以上时间和空间复杂度均为O(VN)
优化空间复杂度为O(N):
F[0..v]←0
for i←1 to N
for v←V to Ci
F[v]←max{F[v],F[v-Ci]+Wi}
注意和上面一个伪代码的差别第一个是从V到Ci,第二个是Ci到V。
第一个代码中的i和i-1保证了第i个状态是由i-1个状态求出来的,
但是如果对第二个方法使用同样的顺序,那么
eg:
当那i=1,V=4,假设C1=1
那么对第一个循环i=2,对v=1 to 4
F[1]←max{F[1],F[1-1]+W1}
F[2]←max{F[2],F[2-1]+W1} F[2-1]+W1=F[1]+W1,此处等号右边的F[1]相当于F[2,1]
F[3]←max{F[3],F[3-1]+W1} F[3-1]+W1=F[1]+W1,此处等号右边的F[2]相当于F[2,2]
...
那么,此时F[v-Ci]+Wi并不是上一个状态F[i-1,v-Ci]得来的,而是由本次状态F[i,v-Ci]得来的
那么对第一个循环i=2,取v=4 to 1
F[4]←max{F[4],F[4-1]+W1}
F[3]←max{F[3],F[3-1]+W1} F[3-1]+W1=F[2]+W1,此处等号右边的F[2]相当于F[1,2]
F[2]←max{F[2],F[2-1]+W1} F[2-1]+W1=F[2]+W1,此处等号右边的F[1]相当于F[1,1]
...
那么,此时F[v-Ci]+Wi(i=2)都是由上一个状态F[i-1,v-Ci](即F[1,V-Ci])得来的
最后形成的伪代码为
def ZeroOnePack(F,C,W)
for v←V to C
F[v]←max{F[v],F[v-C]+W}
F[0..V]←0
for i←1 to N
ZeroOnePack(F,Ci,Wi)
(a)恰好把背包装满
在初始化时除了F[0]应该为0,其他F[1...V]应该为-∞
(b)不要求把所有背包装满
F[0...V]全为0
分析:
F[v]=0表示当剩余体积为v时,价值为0;
F[0]=0表示背包装满了(剩余体积为0),但价值为0,说明背包什么也没装(开始体积为0,剩余体积也就为0);
F[v]=0(v≠0)表示背包没装满,价值为0,是一个合法的状态;F[v]=-∞,是一个总会得到更新的状态。
(a)F[v]=0,而v≠0,说明在剩余体积为v时都没有装东西,那么在状态的转移过程中,是可以F[V+Ci]=max{F[v+Ci],F[v]},这就相当于从一个没有装满的合理的状态出发,继续装东西。
(b)将F[1...V]设为-∞,F[i,v]=max{value,-∞},总是在第一次状态转移的时候就往里面装东西了,之后的状态转移都是从之前装了相应价值物品的状态出发的。
二、完全背包
(1)问题描述:有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。放入第 i 种物品的费用是 Ci,价值是 Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包容量,且价值总和最大。
状态转移方程为:
F[i,v]=max{F[i-1,v-kCi]+kWi,0≤kCi≤v}(k=1,2,...,V/Ci)
总的时间复杂度为O(NVΣ(O/Ci))
(2)转换为01背包问题
(a)把第i件物品转化为⌊V /Ci⌋ 件费用及价值均不变的物品,然后求解这个01背包问题。
(b)把第i种物品拆成费用为
,价值为
的若干件物品,其中k为
≤V的非负整数,总复杂度变为O(NVlog
⌊V /Ci⌋)
(3)伪代码:
F[0...V]←0
for i←1 to N
for v← Ci to V
F[v]←max(F[v],F[v-Ci]+Wi)
考虑之前的01背包问题,v是从V转移懂Ci的,因此此时,每一步都是由本次状态过来的,即F[i,...]是可以重复多次减去Ci,即多次取到第i个物品
此时的状态转移方程等价于
F [i,v] = max(F [i − 1,v],F [i,v − Ci] + Wi)
最终形成的伪代码为:
ef CompletePack(F,C,W)
for v←C to V
F[v]←max{F[v],F[v-C]+W}
(a)问题描述:有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 Mi 件可用,每件耗费的空间是 Ci,价值是 Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
基本的状态转移方程为
F[i,v]=max{F[i,v],F[i-1,v-k*Ci]+k*Wi | 0≤k≤Mi}
总的时间复杂度为O(NVΣMi)
(2)转化为01背包
2进制思想:将第i中物品分成若干件01背包中的物品,没个物品的系数变为。例如,Mi=13,则k=3,系数被分为1,2,4,6。总复杂度变为了O(NVlogMi)。
(3)最后的伪代码为:
def MultiplePack(F,C,W,M)
if C·M≥V
CompletePack(F,C,W)
return
k←1
while k < M
ZeroOnePMack(kC,kW)
M←M-k
k←2k
ZeroOnePack(C·M,W·M)