PKU数据结构与算法Python版--习题总结(10)--博物馆大盗

本文介绍了如何解决博物馆大盗问题,即在给定宝物列表和背包最大承重的情况下,通过动态规划和递归方法找到最高总价值及对应的宝物选择。文中提供了三种代码实现,包括使用字典、二维列表和递归加函数值缓存,并分别进行调用检验。
摘要由CSDN通过智能技术生成

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}, {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值