题目:有N件物品和一个容量为W的背包。第i件物品的重量是 w[i],价值是 v[i]。
求将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
最简单的一类“01背包”:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:f[i][j] 表示前 i 件物品放入一个容量为j的背包可以获得的最大价值。
状态转移方程:
if (w[i] > j) f[i][j] = f[i-1][j];
if (w[i] <= j) f[i][j] = max{ f[i-1][j], f[i-1][j-w[i]] + v[i] };
该方法的时间和空间复杂度均为 O(N*W),但空间复杂度可以优化到 O(N)。
f[i][j] 是由 f[i-1][j] 和 f[i-1][j-w[i]] 两个子问题递推而来,事实上,在每次主循环中
我们以 j = W -> 0 的顺序推 f[j],这样就能能保证推 f[j] 时 f[j-w[i]] 保存的是状态 f[i-1][j-w[i]]的值。
伪代码如下:
for i = 0 -> W
f[i] = 0;
for i = 1 -> N
for j = W -> w[i]
f[j] = max{ f[j], f[j-w[i]] + v[i] };
可以发现其中一些 f[i][j] 没有必要求出来,可以用递归来实现,免去一些不必要的 f[i][j] 的计算,
能节约一些时间:
/*
先初始化 f[0][0...W] = f[0...N][0] = 0; 其余的 f[i][j] = -1;
然后调用 knapsack(N, W);
*/
int knapsack(int i, int j)
{
if ( f[i][j] < 0 )
{
if ( j >= w[i] )
f[i][j] = max( knapsack(i-1, j), knapsack(i-1, j-w[i]) + v[i] );
else
f[i][j] = knapsack(i-1, j);
}
return f[i][j];
}
第二类“完全背包问题”:每种物品都有无限件可用,对于每种物品可以取0件、取1件、取2件.....
对于完全背包问题,其中最优算法时间复杂度是O(N*W),算法和上面的算法非常相似:
for i = 1 -> N
for j = w[i] -> W
f[j] = max{ f[j], f[j-w[i]] + v[i] };
至于其中的道理,自己去体会吧。
第三类“多重背包问题”:
第四类“混合三种背包问题”:(详见百度百科)
背包问题
最新推荐文章于 2023-07-18 09:24:27 发布