动态规划学习-【0-1背包问题的优化和变种】
0-1背包问题的基本描述和解题算法请看:
动态规划学习-【0-1背包问题】
上面这篇文章我们已经得出0-1背包问题的状态转移方程:
F(n, C)考虑将n个物品放进容量为C的背包,使得价值最大:
F(i, C) = max( F(i - 1, C), v(i) + F(i - 1, C - w(i)))
1. 空间复杂度为:O(2 * C)
根据状态转移方程,我们可以分析出来,第i行元素只依赖于第i-1行元素。理论上,只需要保持两行元素。空间复杂度变为:O(2 * C) = O(C)。
具体操作:
首先,初始化i = 0 行的数值。(行表示物品索引,列表示背包剩余容量)
其次,i = 1 这一行的数据要根据i = 0行的数据进行计算。

然后,计算 i = 2这一行,i = 2这一行依赖于i = 1行。针对这一步,其实我们有两种操作办法:第一种,把 i = 1 行往上移动,覆盖原有的i = 0行数据,i = 2行覆盖i = 1行;第二种,把 i = 2行直接覆盖 i = 0行。我们采用第二种方法,如果采用第一种方法会导致时间耗费增加。

接着, i = 3行可以直接覆盖掉i = 1行。

最后,根据以上操作,我们会发现上面行永远在记录i = 偶数的行、下面行永远在记录 i = 奇数的行。

在实际编程中,我们计算第i行,它依赖于第 i - 1行,我们直接根据i是奇数和偶数来进行取值计算。
class Solution:
def knapsack01(self, w, v, C):
assert len(w) == len(v)
if len(w) == 0 or len(v)== 0:
return 0
memo = [[0 for i in range(C + 1)] for _ in range(2)]
for i in range(C + 1):
memo[0][i] = v[0] if i >= w[0] else 0
for i in range(len(v)):
for j in range(C + 1):
memo[i % 2][j] = memo[(i - 1) % 2][j]
if j >= w[i]:
memo[i % 2][j] = max(memo[i % 2][j], v[i] + memo[(i - 1) % 2][j - w[i]])
return memo[(len(v) - 1) % 2][C]
solution = Solution()
w = [1, 2, 3]
v = [6, 10, 12]
print(solution.knapsack01(w, v, 5))
2. 空间复杂度为:O(C)
第 i 行元素只依赖于第 i-1 行元素。理论上,只需要保持两行元素。空间复杂度为:O(2 * C) = O ( C )。
思考:只使用一行大小为C的数组完成动态规划?
我们再来分析一下,当我们使用两行空间时候的计算过程。第 0 行已经初始化完成,第 1 行已经计算了一半,当C = 2时,已经可以容纳id = 1的物品。这时,我们要考虑我们的状态转移方程,F(1, 2) = max(F(0, 2), 10 + F(0, 0))。我们可以注意一下,F(1, 2)只和它左边和上边的数值有关,和 i = 0 行的右边元素无关。因此,我们可以考虑在这个点上做优化。

当我们只有i = 0 这一行内容时,因为 i =1行只与i = 0行的左边内容有关。因此,当我们计算i = 1行时,可以考虑从i = 0 行的右边开始刷新内容。
下图是计算F( 1, 5) = max(F(0, 5), 10 + F(0, 5 - 2))

下图是计算F(1, 4) = max(F(0, 4), 10 + F(0, 4 - 2))

下图是计算F(1, 3) = max(F(0, 3), 10 + F(0, 3 - 2))

下图是计算F(1, 2) = max(F(0, 2), 10 + F(0, 2 - 2))

由于F(1, 1)中背包所容纳重量已经不能放置 i = 1 的物品,所以 F(1 , 1)左边所有表格都不用再更新。这样的话,可以提前终止循环判断,提高了时间效率。

代码实现:
class Solution:
def knapsack01(self, w, v, C):
assert len(w) == len(v)
if len(w) == 0 or len(v)== 0:
return 0
memo = [0 for i in range(C + 1)]
for i in range(C + 1):
memo[i] = v[0] if i >= w[0] else 0
for i in range(1,len(v)):
for j in range(C, -1, -1):
if j >= w[i]:
memo[j] = max(memo[j], v[i] + memo[j - w[i]])
else:
continue
return memo[C]
solution = Solution()
w = [1, 2, 3]
v = [6, 10, 12]
print(solution.knapsack01(w, v, 5))
3. 0-1 背包问题的变种
3.1 完全背包问题:每个物品可以无限使用。
虽然每个物品可以无限使用,但是背包的容量是有限的。背包可以装有限的物品,只是一些物品是重复的。
3.2 多重背包问题:每个物品不止1个,有num(i)个。
3.3 多维费用背包问题:要考虑物品的体积和重量两个维度?
3.4 物品之间加入约束
- 物品之间可以相互排斥。在背包中放入某个物品之后,就不能放入其他物品。
- 物品之间的相互依赖。在背包中放入某个物品,就必须放入另一个物品。
更多推荐
所有评论(0)