有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
则其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]};
伪代码如下:
for i = 1...N
for v = 0...V
f[i][v] = max(f[i-1][v], f[i-1][v-c[i]] + w[i]);
优化1:可在两层循环中直接输入c[i],w[i],而不需要进行保存。
优化2:考虑到f[i+1]只是直接依赖于f[i],类比优化一,并且f[v]依赖于f[v-c[i]],由此得到改进如下:
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
当f[v]直接取f[v]时对应的是没有取第i个物品,反之则取了。初始化f[]数组为0,相当于对于前0个物品,最大价值都为0,成立。
01背包问题的算法在更复杂的背包问题中将用到,现将其封装为:
procedure ZeroOnePack(cost,weight)
for v=V..cost
f[v]=max{f[v],f[v-cost]+weight}
即决策是否要选择该物品。如此调用:
for i = 1...N
ZeroOnePack(c[i],w[i]);
初始化的细节:
当要求恰好装满背包时,可以将f[0]初始化为0,而其他f[1...V]均设置为-∞,由此当f[V]算得为负时候其实是没有正确答案的。
也可以将除f[0]外f[1...V]全部设置为-1,每次如果f[v]或f[v-c]为-1,则对相应状况不做处理,因为-1表示的是未定义状态。
(看 《背包九讲》 的心得)