题目描述:
问题:0-1背包问题
描述:有一个背包,它的容量为C(Capacity),现在有n种不同的物品编号分别为0...n-1,其中每一件物品的重量为w(i),价值为v(i)。问可以向这个背包中盛放哪些物品,使得在不超过背包容量的基础上,物品的总价值最大。
解题思路:
(1)暴力解法:
每一件物品都可以放进背包,也可以不放进背包,有种情况。我们再从这
种情形中选出总价值最大的物品,时间复杂度为O(
)。
(2)贪心算法解法:
贪心算法:用物品的价值除以重量得到单位物品的价值。贪心算法就是优先放入平均价值最高的物品。
第0件物品的单位价值为6,第1件物品的单位价值为5,第2件物品的单位价值为4。我们首先放单位价值最高的物品0号,它的重量是1。接着放入单位价值次大的物品1号,它的重量是2,。背包中还能容下2个重量,由于编号为2的物品重量是3,所以不能放入到背包中。所以贪心算法得到的背包中最多能容纳16价值的物品。

很明显,对于这个问题,我们只放入编号为1和2的物品,总价值是最大的。与贪心算法恰恰相反的是,我们刚好放弃了单位价值最大的编号为0的商品。所以这个例子证明,贪心算法是无法解决这样的问题。

(3)动态规划解法:
动态规划解题思路:
1. 状态转移方程:
之前接触到的动态规划题目涉及到的状态都可以用一个变量(或者参数)解决问题。通常问题中含有的参数,决定了问题需要满足的约束条件。对于当前的背包问题,也是求一个最优解的问题。对于这个问题,我们两个约束条件,第一个约束条件是在n个物体里选择,第二个约束条件是选定的容量要小于等于给定的C值。求解价值最大的函数设为F(n, C),F函数传入两个参数n和C。F(n,C)函数的定义是:考虑将n个物品放进容器为C的背包,使得价值最大。

我们知道问题的状态转移方程之后,可以使用自顶向下的方式(递归法)解决这个问题;也可以对递归法进行优化使用记忆化搜索的方式解决这个问题;最后我们写出自底向上的动态规划解法。
1)递归方法实现
class Solution:
def knapsack01(self, w, v, C):
return self.bestValue(w, v, len(w)-1, C)
def bestValue(self, w, v, index, C):
if index < 0 or C <= 0:
return 0
res = self.bestValue(w, v, index - 1, C)
if C >= w[index]:
res = max(res, v[index] + self.bestValue(w, v, index - 1, C - w[index]))
return res
solution = Solution()
w = [1, 2, 3]
v = [6, 10, 12]
print(solution.knapsack01(w, v, 5))
2)记忆化搜索方法实现
class Solution:
def __init__(self):
self.memo = []
def knapsack01(self, w, v, C):
self.memo = [ [-1 for i in range(C+1)] for _ in range(len(w))]
return self.bestValue(w, v, len(w)-1, C)
def bestValue(self, w, v, index, C):
if index < 0 or C <= 0:
return 0
if self.memo[index][C] != -1:
return self.memo[index][C]
res = self.bestValue(w, v, index - 1, C)
if C >= w[index]:
res = max(res, v[index] + self.bestValue(w, v, index - 1, C - w[index]))
self.memo[index][C] = res
return self.memo[index][C]
solution = Solution()
w = [1, 2, 3]
v = [6, 10, 12]
print(solution.knapsack01(w, v, 5))
上面代码不能体现出Python语言的简洁性,代码优化后为:
class Solution:
def knapsack01(self, w, v, C):
memo = [ [-1 for i in range(C+1)] for _ in range(len(w))]
def bestValue(w, v, index, C):
if index < 0 or C <= 0:
return 0
if memo[index][C] != -1:
return memo[index][C]
res = bestValue(w, v, index - 1, C)
if C >= w[index]:
res = max(res, v[index] + bestValue(w, v, index - 1, C - w[index]))
memo[index][C] = res
return memo[index][C]
return bestValue(w, v, len(w)-1, C)
solution = Solution()
w = [1, 2, 3]
v = [6, 10, 12]
print(solution.knapsack01(w, v, 5))
3)动态规划方法实现
根据上面分析出来的状态转移方程,我们可以做出如下表格:

根据上面表格,用Python实现动态规划解法如下:
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(len(w))]
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][j] = memo[i - 1][j]
if j >= w[i]:
memo[i][j] = max(memo[i][j], v[i] + memo[i - 1][j - w[i]])
return memo[-1][-1]
solution = Solution()
w = [1, 2, 3]
v = [6, 10, 12]
print(solution.knapsack01(w, v, 5))
时间复杂度为:O(n * C)
空间复杂度为:O(n * C)
5546



被折叠的 条评论
为什么被折叠?



