动态规划之背包问题
1.01背包问题
- 题目描述:
有N件物品和一个容量为V的背包。放入第 i i 件物品耗费的空间是 ,得到的价值是 Wi W i ,求解将哪些物品装入背包可使价值总和最大。
- 状态转移方程:
F[i][v] F [ i ] [ v ] = max(F[i−1][v],F[i−1][v−Ci]+Wi) m a x ( F [ i − 1 ] [ v ] , F [ i − 1 ] [ v − C i ] + W i )
在 i−1 i − 1 ,重量为 v v 的状态下,放或者不放第件物品。
- 将二维转化至一维可得伪代码:复杂度: O(VN) O ( V N )
void ZeroOnePack(F,C,W)
{
for v = V to C //从V到C是进行了优化
F[v]=max{F[v],F[v-C]+W};
}
for i = 1 to N
ZeroOnePack(F,C[i],W[i]);
- 初始化的细节问题:
如果要求恰好装满背包,则初始化时, F[0]=0 F [ 0 ] = 0 ,其余 F[1……V]=−∞ F [ 1 … … V ] = − ∞
若未要求恰好装满,则 F[0……V]=0 F [ 0 … … V ] = 0 。
- 个人对于代码的理解:
i i 从1到n刷新数组,所以每一个在使用数组时,都有 i−1 i − 1 使用过的痕迹,所以起到了状态的限制。
另外, v v 从V到0,当计算较大的时,较小的 v v 还没有放入,所以保证了所有的物品只被放入一次。
2.完全背包问题
- 题目描述:
有N种物品和一个容量为 V V 的背包,每种物品都有无限件可以用,放入第件物品耗费的空间是 Ci C i ,得到的价值是 Wi W i 。求解:将哪些物品放入背包,可使这些物品耗费的空间总和不超过背包的容量,且价值总和最大。
- 状态转移方程:
F[i][v] F [ i ] [ v ] = max(F[i−1][v],F[i−1][v−k∗Ci]+k∗Wi) m a x ( F [ i − 1 ] [ v ] , F [ i − 1 ] [ v − k ∗ C i ] + k ∗ W i )
F[i][v] F [ i ] [ v ] = max(F[i−1][v],F[i][v−Ci]+Wi) m a x ( F [ i − 1 ] [ v ] , F [ i ] [ v − C i ] + W i )
- 可能的优化方法:
去掉重量大于V的,对于cost相同的物品,只选择weight最大的
- 伪代码: O(VN) O ( V N )
void CompletePack(F,C,W)
{
for v = C to V
F[v]=max{F[v],F[v-C]+W};
}
for i = 1 to N
CompletePack(F,C[i],W[i]);
- 个人对于代码的理解:
使 v v 从C到V,当较大时,较小的 v v 已经被更新过,所以相当于可以重复往背包里放入同一个物体。
3.多重背包问题
- 题目描述:
有N件物品和一个容量为V的背包。第种物品最多有 Mi M i 件可用,每件耗费的空间是 Ci C i ,价值是 Wi W i 。求解将哪些物品装入背包可使这些物品耗费的空间总和不超过背包容量,且价值总和最大。
- 状态转移方程:
F[i][v] F [ i ] [ v ] = max(F[i−1][v],F[i−1][v−k∗Ci]+k∗Wi)0≤k≤Mi m a x ( F [ i − 1 ] [ v ] , F [ i − 1 ] [ v − k ∗ C i ] + k ∗ W i ) 0 ≤ k ≤ M i
- 伪代码: O(V∑logMi) O ( V ∑ l o g M i )
void MultiplePack(F,C,W,M)
{
if(C*M>=V){
CompletePack(F,C,W);
return ;
}
k=1;
while(k<M){
ZeroOnePack(F,k*C,k*W);
M=M-k;
k=k*2;
ZeroOnePack(F,M*C,M*W);
}
}
- 代码理解:
将多重背包转化为完全背包和01背包。如果物品对于背包不能视作完全,就将物品拆成1,2,4,8……的01背包。