一.背包问题概述:
给定 n 种物品和一个容量为 capacity 的背包,其中每一个物品的重量和价值已知 。
问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?
二.分析过程:
1.思路:
对于每一个物品只有两种选择,第一种情况:装入当前物品;第二种情况:不装入当前物品。
我们从第一个物品开始,将其重量和背包容量进行比较,如果比背包容量小,则选择将这个物品装入背包,记录它的价值(如果比背包容量大,忽略这个物品的重量和价值,直接比较下一个物品);再拿下一个物品的重量与当前背包容量比较(此时背包容量为背包的总容量减去已经装入背包的物品重量),如果比当前背包容量小,则选择将这个物品装入背包,将这个物品的价值与已经装入背包的物品价值相加(如果比背包容量大,忽略这个物品的重量和价值,直接比较下一个物品)......以此类推。
2.具体实例:
题目:
假设一共有 4 个物品,重量分别为 weights = [2, 3, 6, 7],对应的物品价值为values = [1, 4, 5, 6],背包容量为 8,应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?
解析:
初始化一个二维矩阵,i 为当前物品,j 为背包容量,dp[ i ][ j ] 即为在第 i 件物品时,背包容量为 j 时所能获得的最大价值。
看下述列表,第一行 0 ~ 8 为背包的容量,第一列 [0, 0], [1, 2], [4, 3], [5, 6], [6, 7],其中第一个数为物品的价值,第二个数为物品的重量。将物品的重量依次与背包容量相比较。
我们以 [5, 6] 这一排举例,此时一共有 3 种物品,重量分别为2, 3, 6:
(1) 当背包容量为 0 和 1 时,最小重量 2 的物品也装不下,此时价值为 0;
(2) 当背包容量为 2 时,能装下重量为 2 的物品,此时其价值为最大价值,为 1;
(3) 当背包容量为 3 时,能装下重量为 2 或 3 的物品,3 物品的价值为 4,大于 2 物品的价值,此时 3 物品的价值为最大价值,为 4(因为重量 3 + 2 = 5 大于此时背包容量,不能将两个物品都放入背包,因此不能将它们的价值相加);
(4) 当背包容量为 4 时,能装下重量为 2 或 3 的物品,3 物品的价值为 4,大于 2 物品的价值,此时 3 物品的价值为最大价值,为 4(因为重量 3 + 2 = 5 大于此时背包容量,不能将两个物品都放入背包,因此不能将它们的价值相加);
(5) 当背包容量为 5 时,能装下重量为 2 和 3 的物品,此时不能再装入重量为 6 的物品,背包容量不够,因此此时最大价值为重量为 2 和 3 的物品价值相加,1 + 4 = 5,此时最大价值为5;
......
以此类推。
物品价值和重量\背包容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0,0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1,2 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
4,3 | 0 | 0 | 1 | 4 | 4 | 1+4 | 1+4 | 1+4 | 1+4 |
5,6 | 0 | 0 | 1 | 4 | 4 | 1+4 | 5 | 5 | 1+5 |
6,7 | 0 | 0 | 1 | 4 | 4 | 1+4 | 5 | 6 | 6 |
物品价值和重量\背包容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0,0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1,2 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
4,3 | 0 | 0 | 1 | 4 | 4 | 5 | 5 | 5 | 5 |
5,6 | 0 | 0 | 1 | 4 | 4 | 5 | 5 | 5 | 6 |
6,7 | 0 | 0 | 1 | 4 | 4 | 5 | 5 | 6 | 6 |
代码解释:
(1)values[i - 1] + dp[i - 1][j - weights[i - 1]]:
这是背包容量可以放下第 i 件物品的情况,并且考虑拿这个物品是否会获得最大价值,dp[i - 1][j - weights[i - 1]]表示在第 i - 1 个物品,背包容量为 j - weights[i - 1]时的最大价值,背包容量减去要拿的物品的重量。
(2)dp[i - 1][j]:
这是背包容量放不下第 i 件物品的情况,选择不拿,不计算其价值。
三.Python代码实现:
def bagProblem(weights, values, capacity):
'''
:param weights: 物品重量
:param values: 物品价值
:param capacity: 总容量
:return:装入背包中的物品的最大总价值
'''
# 获取weights列表的长度
n = len(weights)
# 初始化一个所有元素都为0的矩阵,然后逐行填充矩阵的值
dp = [[0 for _ in range(capacity + 1)] for _ in range(n + 1)]
# 遍历物品,将每一个物品的重量和当前背包容量相比较
for i in range(1, n + 1):
# 遍历背包容量
for j in range(1, capacity + 1):
# 如果当前物品的重量小于等于背包容量
if weights[i - 1] <= j:
# 选择当前物品和不选择当前物品的最大值(上述分析过程中有对这句代码的具体解释)
dp[i][j] = max(values[i - 1] + dp[i - 1][j - weights[i - 1]], dp[i - 1][j])
else:
# 如果当前物品的重量大于背包容量,则不选择当前物品
dp[i][j] = dp[i - 1][j]
return dp[n][capacity]
# 物品的重量,价值和背包容量
values = [1, 4, 5, 6]
weights = [2, 3, 6, 7]
capacity = 8
# 计算最大价值
max_value = bagProblem(weights, values, capacity)
print("最大价值为:", max_value)