动态规划问题
例如: 你有一个背包,最大承受重量为max_weight,有一些宠物,每个宠物有自己的重量,每个宠物能给你带来的价值不同,现在要背着宠物去旅行,目的是使价值最大化。(其他问题:例如给一定量的本金,去买房子,每个房子的售价和给你带来的收益不一样,求使收益最大化的买房策略)
宠物的重量
宠物名 | 重量weight |
---|---|
cat | 1 |
bird | 1 |
dog | 2 |
tiger | 2 |
elephant | 3 |
宠物的价值
宠物名 | 价值Value |
---|---|
cat | 3 |
bird | 6 |
dog | 5 |
tiger | 9 |
elephant | 10 |
先看下面的表格,第i行第j列元素表示:在背包容量为j的情况下,能选择的宠物为0-i,所能带来的最大价值
举个例子,[3][2]表示:背包容量为2,只能选cat,bird,dog这三种宠物,所带来的最大价值
宠物名/背包重量 | 1 | 2 | 3 | 4 | 5 | … | max_weight |
---|---|---|---|---|---|---|---|
cat | |||||||
bird | |||||||
dog | |||||||
tiger | |||||||
elephant |
动态规划就是将物品种类和背包重量逐步增加,来求解问题。
每新增一种宠物选项i+1时,有两种情况,
一种是不选择这个宠物,则最大价值为背包重量不变的情况下,选择0-i种宠物带来的最大价值
另一种情况是,选择这种宠物i+1,则剩余空间为max_weight - weight[i+1],则最大价值为这个宠物的价值+剩余空间选择0-i种宠物带来的最大价值
取这两种情况的最大值为最终结果,
举个例子:计算[3][2]时,dog为新增宠物选项,
情况一:不选择dog,则最大价值仍然为max_weight=2时,可选项是cat,bird时的最大收益,即[2][2]
情况二:选择dog,剩余空间为max_weight-dog的weight = 0,则在剩余空间为0,可选择cat,bird时,带来的最大价值为0(这里因为没有空间了,正常是[i][max_weight-dog的weight])
不带注释的版本
import numpy as np
value_dict = {"cat":3, "bird":6, "dog":5, "tiger":9, "elephant":10}
weight_dict = {"cat":1, "bird":1, "dog":2, "tiger":2, "elephant":3}
value_list = [3, 6, 5, 9, 10]
weight_list = [1, 1, 2, 2, 3]
def dp_matric(pet_lens, max_weight, value_list, weight_list):
max_value_matrix = np.zeros((pet_lens, max_weight), dtype=np.dtype((np.str_,500)))
for pet_i in range(pet_lens):
for bag_weight in range(max_weight):
c1 = max_value_matrix[pet_i-1][bag_weight] if pet_i > 0 else 0
weight_i = weight_list[pet_i]
value_i = value_list[pet_i]
if bag_weight+1 < weight_i :
c2 = max_value_matrix[pet_i-1][bag_weight] if pet_i > 0 else 0
else:
resi = max_value_matrix[pet_i-1][bag_weight-weight_i] if pet_i > 0 and bag_weight-weight_i >= 0 else 0
c2 = value_i + int(resi)
max_value_matrix[pet_i][bag_weight] = c1 if int(c1) > int(c2) else c2
return max_value_matrix
print(dp_matric(len(weight_dict.items()), 6, value_list, weight_list))
带注释的版本
import numpy as np
value_dict = {"cat":3, "bird":6, "dog":5, "tiger":9, "elephant":10}
weight_dict = {"cat":1, "bird":1, "dog":2, "tiger":2, "elephant":3}
value_list = [3, 6, 5, 9, 10]
weight_list = [1, 1, 2, 2, 3]
# pet_i表示宠物下标,只能选择pet_i以及pet_i之前的宠物
# bag_weight为背包容量大小
def dp_matric(pet_lens, max_weight, value_list, weight_list):
# 初始化最大价值矩阵
max_value_matrix = np.zeros((pet_lens, max_weight), dtype=np.dtype((np.str_,500)))
for pet_i in range(pet_lens):
# bag_weight的下标从0开始,但实际表示的weight是从1开始, bag_weight表示的背包容量为bag_weight+1
for bag_weight in range(max_weight):
# c1为不考虑pet_i的最大价值
# 放入pet_i之前的最大价值,如果pet_i为第一个可选择的宠物,则之前的最大价值为0
c1 = max_value_matrix[pet_i-1][bag_weight] if pet_i > 0 else 0
# pet_i的权重和价值
weight_i = weight_list[pet_i]
value_i = value_list[pet_i]
# c2为考虑pet_i的情况,又分背包能不能放下pet_i两种情况
# 因为矩阵下标从0开始,bag_weight表示的背包容量为bag_weight+1
if bag_weight+1 < weight_i :
# 背包放不下pet_i
c2 = max_value_matrix[pet_i-1][bag_weight] if pet_i > 0 else 0
else:
# 背包能放下pet_i
# 放入pet_i, 最大价值=pet_i的价值+(max_weight-pet_i的weight)剩余空间的最大价值
# resi为剩余空间的最大价值
# 如果pet_i为第一个可选择的宠物,无论剩余空间大小为多少,没有其他宠物可选,故剩余空间的最大价值为0
# bag_weight-weight_i代表的剩余空间大小为bag_weight-weight_i+1, 只有剩余空间>=1时(即bag_weight-weight_i>=0),该空间才能有价值,否则为0
resi = max_value_matrix[pet_i-1][bag_weight-weight_i] if pet_i > 0 and bag_weight-weight_i >= 0 else 0
c2 = value_i + int(resi)
max_value_matrix[pet_i][bag_weight] = c1 if int(c1) > int(c2) else c2
return max_value_matrix
# max_value_matrix[i][j]表示空间大小为j+1,能取的物品为value_list[0:i], 所能取到的最大价值
print(dp_matric(len(weight_dict.items()), 6, value_list, weight_list))
背包容量为6时的最大价值矩阵如下:
如果宠物的顺序改变,中间的生成过程会不同,但最后的结果是一样的
一般要求的是最终结果,即图中的25