问题
有n种物品,每种只有一件,物品具有价值和体积两个属性
一个容量为v的背包
每种物品可以选择是否放入背包中
选择合适的策略,使得背包中的物品总价值最高
思路
定义d[i][v]
表示处理前i个物品,背包剩余容量为v时背包的最大价值
状态转移方程:
f[i][v] = max(f[i-1][v], d[i-1][v-c[i]]+w[i])
如果不装第i个物品,则f[i][v] = d[i-1][v]
如果装第i个物品,则价值增加w[i]
,背包容量减少c[i]
将f[i][v]
优化为一维数组
可以看出推导f[i][v]
时需要f[i-1][v]
和f[i-1][v-c[i]]
如果我们可以保证第i次循环时f[v]
里保存的时上一次的也就是i-1
的f[v-c[i]]
,那么我们就可以用一维数组f[v]
代替f[i][v]
,代码如下
for(i=1;i<=n;i++)
for(v=V;v>=0;v--)
//由于f[v]是逆序遍历的,当更新f[v]是f[v-v[i]]仍是上次的结果,此时f[v-c[i]]相当于f[i-1][v-v[i]]
f[v] = max(f[v],f[v-c[i]]+w[i]);//背包剩余容量为v时可以装的物品的最大价值
必须恰好装满背包
初始化时设置f[0]=0
其他为-inf
,这样可以保证最终得到的f[N]
是恰好装满时的解
初始化的f数组事实上是没有任何物品可以放入背包时的状态,此时f[0]=0
而其他是没有合法解,定义为-inf
不要求必须把背包装满
初始化时设置f[0..V]=0
一个常数优化
前面的伪代码中有 for v=V..1
,可以将这个循环的下限进行改进。
由于只需要最后f[v]
的值,倒推前一个物品,其实只要知道f[v-w[n]]
即可。以此类推,对第j个背包,其实只需要知道到f[v-sum{w[j..n]}]
即可,即代码中的
for i=1..N
for v=V..0
可以改成
for i=1..n
bound=max{V-sum{w[i..n]},c[i]}
for v=V..bound
这对于V比较大时是有用的。