背包问题

    技术2022-05-19  20

    题目:有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] }; 至于其中的道理,自己去体会吧。 第三类“多重背包问题”: 第四类“混合三种背包问题”:(详见百度百科)

    最新回复(0)