假设一个包载重量为m,有n个物品,重量为w_i,价值为v_i,1 ≤ i ≤ n,要求把物品装入背包,并使包内物品价值最大。
(参考文献:张玲玲.《python算法详解》[M].北京:北京邮电出版社.11.14背包问题, 292-294.)。
这个问题可以产生很多的变形,比如
游客打卡问题,把物品换成打卡点,重量换成需要时间,价值换为奖励分数,载重量换为时间;
小人打仗、走路问题,把物品换成攻击对象,重量换成需要能量值,价值换为造成伤害,载重量换为总能量值……
学会才能以不变应万变啊,下面先展示代码。
class Bag:
def bag(self, n, m, w, v):
res = [[0 for _ in range(m+1)] for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, m+1):
if j >= w[i - 1]:
res[i][j] = max(res[i-1][j], res[i-1][j-w[i-1]] + v[i-1])
else:
res[i][j] = res[i-1][j]
return res[n][m]
"""
res = [[0, 0, 0, 0, 0, 0, 0],
[0, 0, 4, 4, 4, 4, 4],
[0, 0, 35, 35, 39, 39, 39],
[0, 43, 43, 78, 78, 82, 82],
[0, 43, 43, 78, 78, 88, 88]]
"""
if __name__ == "__main__":
n, m, w, v = 4, 6, [2, 2, 1, 2], [4, 35, 43, 10]
# 变量介绍:
# 物品数:n,载重量:m,每个物品的重量list: w,每个物品对应的价值list:v。
# 打卡点数 , 总时间数,每个打卡点需要时间list,打卡奖励分值list
# 敌人数,总活力值,每步所需时间list,造成伤害list。
print(Bag().bag(n, m, w, v))
1. 以求解母题“背包问题”为例,解题关键点:
1)推理递推公式。即如何确定放入当前物品后 相比于 放入当前物品前 哪个总价值更大。判断之后,即可获取某一总重量下的最大价值。然后就可以不断在后序的步骤中被调用。
递推公式:
公式理解:
1️⃣ 当前最大价值 = max{ 不放当前物品 i 的最大价值, 放了当前物品 i 的最大价值 }
2️⃣ 放了当前物品 i 的最大价值 = 当前物品的价值 v[i-1] (其中i-1是为了获取列表中对应角标的价值) + 不放当前物品 且 载重条件满足下的最大价值
3️⃣不放当前物品 且 载重条件满足 = 当前总载重 j - 当前物品 i 所占用重量 w[i-1] (其中i-1是为了获取列表中对应角标的重量)
2)构建二维列表做记录,运行时遍历的方向为 逐行从左往右。这个思路和从左上角走到右下角问题的求解思路相似。注意要多出来一列初始化为0的列。
2. 为什么遍历的方向为 逐行从左往右
因为要获取不放当前物品 i 的最大价值,相当于要获取i-1行 的最大价值, 其对应角标也得是 j ,所以先生成出来,因此从整体上看是逐行从左往右的。
增加一个show函数,从res右下角向左上角遍历即可。
class Solution:
def bag(self, n, m, w, v):
res = [[0 for _ in range(m+1)] for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, m+1):
if j > w[i-1]:
res[i][j] = max(res[i-1][j], v[i-1]+res[i-1][j-w[i-1]]) # 减去当前重量的剩余重量的最大价值
else:
res[i][j] = res[i-1][j]
self.show(n, m, w, res) # 展示选取的物品编号, 会得到一个逆序的序号列表:[3, 2, 1]
return res[n][m] # 返回结果:88
def show(self,n, m, w,res):
path = []
j = m
for i in range(n, 0, -1): # 思路是从右下角向左上角遍历并判断是否添加了物品
if res[i][j] > res[i-1][j]:
path.append(i-1)
j -= w[i-1] # 减去相应的重量比较
print(path) # path即为所求
if __name__ == "__main__":
n, m, w, v = 4, 6 , [2,2,1,2], [4,35,43,10]
# n 物品数, m 总载重, w重量列表, 价值列表
print(Solution().bag(n, m, w, v ))