1、题目
给定一个宝物列表treasureList = [{‘w’: 2,‘v’: 3}, {‘w’: 3,‘v’: 4}, …]
注意:每样宝物只有1个。
给定包裹最多承重maxWeight > 0,实现一个函数,根据以上条件得到最高总价值以及对应的宝物
参数:宝物列表treasureList,背包最大承重maxWeight
返回值:最大总价值maxValue,选取的宝物列表chosenList(格式同treasureList)
2、分析
maxValue(i,w)代表在最大承重w前提下,在前i个宝物中取得的最大的价值,有如下对应关系:
解释:
1、当没有宝物或者最大承重为0,也即i=0或者w=0时,maxValue(i,w)=0
2、当第i件宝物的重量本身就大于最大承重时,即当weight(i)>w时,maxValue(i,w)=maxValue(i-1,w)
3、当第i件宝物重量小于最大承重时,需要考虑“取该宝物”和“不取该宝物”两种情况
4、代码回溯问题:注意:不能在代码中使用chosenList.append()
3、代码一:动态规划-使用字典存储在每一个(i,w)下最大价值和添加的宝物
def dpMuseumThief(treasureList, maxWeight):
maxValue = 0
chosenList = []
#小技巧:使得tr重点 所有宝物都是从1开始编号
tr=[None]+treasureList
#记录表格
m={
(i,w):[0,None] for i in range(len(tr)) for w in range(maxWeight+1)}
#逐个填写
for i in range(1,len(tr)):
for w in range(1,maxWeight+1):
#第i件宝物重量大于最大承重,不能装进去
if tr[i]['w']>w:
m[i,w][0]=m[i-1,w][0]
#能装进去,要考虑是否要装
else:
if m[i-1,w][0]>m[i-1,w-tr[i]['w']][0]+tr[i]['v']:
m[i,w][0]=m[i-1,w][0]
else:
m[i,w][0]=m[i-1,w-tr[i]['w']][0]+tr[i]['v']
m[i,w][1]=tr[i]
maxValue=m[i,w][0]
while w>0:#回溯这一路走下来一共添加了几件宝物
if m[i,w][1] is not None:
chosenList.insert(0,m[i,w][1])#保持添加顺序不变
w=w-m[i,w][1]['w']
i=i-1
return maxValue, chosenList
4、代码二:动态规划-使用两个二维列表存储在每一个(i,w)下最大价值和添加的宝物
def dpMuseumThief(treasureList, maxWeight):
maxValue = 0
choosenList = []
#代码开始
# 大盗最大承重和可能拿走的宝物最多件数
max_w, max_num = maxWeight, len(treasureList)
#小技巧,使得宝物从1开始计数
treasureList=[None]+treasureList
# 初始化maxvalue和treasure_used,记录在当前可选宝物范围中,大盗可拿走的最大总价值及对应宝物
mtv = [[0 for i in range(max_w+1)] for j in range(max_num+1)]
treasure_used= [[False for p in range(max_w+1)] for q in range(max_num+1)]
# 循环添加宝物至“大盗可选清单”
for i in range(1,max_num+1):
need_w,get_v = treasureList[i]['w'],treasureList[i]['v'] # 添加的宝物的重量和价值
for j in range(1,max_w+1):
if j<need_w: # 如果当前物品重量大于背包容量
mtv[i][j]=mtv[i-1][j] # 和这个物品不存在时一样
else:
# 如果可以放,需要进一步决策:大于上一次选择的最佳方案则更新mtv[i][j]
treasure_used[i][j] = (mtv[i - 1][j - need_w] + get_v > mtv[i - 1][j])
mtv[i][j]=max(mtv[i-1][j-need_w]+ get_v ,mtv[i-1][j])
# 记录在mtv[i][j]时是否选择了放入第i-1件宝物
maxValue=mtv[max_num][max_w] # 回溯打印编号
#以下是回溯得到取得宝物的列表
num=max_num
w=max_w
s = ""
while num > 0:
if treasure_used[num][w]:
choosenList.append(treasureList[num])
w = w - treasureList[num]['w']
num = num - 1 # 处理依据 mtv[i-1][j-need_w]+ get_v
else:
num = num - 1 # 处理依据 mtv[i-1][j]
# 代码结束
return maxValue, choosenList
5、代码一、二的调用检验
# 检验
print("=========== 1 博物馆大盗问题 ============")
treasureList = [[{
'w':2, 'v':3}, {
'w':3, 'v':4}, {
'w':4, 'v':8}, {
'w':5, 'v':8}, {
'w':9, 'v':10}]]
treasureList.append([{
'w':1, 'v':2}, {
'w':2, 'v':2}, {
'w':2, 'v':3}, {
'w':4, 'v':5}, {
'w':4, 'v':6}, {