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

1、题目

给定一个宝物列表treasureList = [{‘w’: 2,‘v’: 3}, {‘w’: 3,‘v’: 4}, …]
注意:每样宝物只有1个。
给定包裹最多承重maxWeight > 0,实现一个函数,根据以上条件得到最高总价值以及对应的宝物
参数:宝物列表treasureList,背包最大承重maxWeight
返回值:最大总价值maxValue,选取的宝物列表chosenList(格式同treasureList)

2、分析

maxValue(i,w)代表在最大承重w前提下,在前i个宝物中取得的最大的价值,有如下对应关系:
PKU数据结构与算法Python版--习题总结(10)--博物馆大盗_第1张图片
解释:
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}, {'w':4, 'v':7}, {'w':5, 'v':7},
                     {'w':5, 'v':8}, {'w':6, 'v':8}, {'w':6, 'v':10}, {'w':7, 'v':10}, {'w':7, 'v':12}, {'w':8, 'v':12}, {'w':8, 'v':13}, {'w':9, 'v':14}, {'w':9, 'v':16}])
treasureList.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':3, 'v':4}, {'w':3, 'v':5}, {'w':4, 'v':6}, {'w':4, 'v':7},
                     {'w':5, 'v':7}, {'w':5, 'v':8}, {'w':6, 'v':8}, {'w':6, 'v':10}, {'w':7, 'v':11}, {'w':7, 'v':12}, {'w':8, 'v':13},
                     {'w':8, 'v':14}, {'w':9, 'v':15}, {'w':9, 'v':16}, {'w':9, 'v':17}, {'w':10, 'v':17}, {'w':10, 'v':18}, {'w':11, 'v':18}])
treasureList.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':3, 'v':4}, {'w':3, 'v':5}, {'w':4, 'v':5}, {'w':4, 'v':6},
                     {'w':5, 'v':6}, {'w':5, 'v':7}, {'w':6, 'v':8}, {'w':6, 'v':9}, {'w':7, 'v':10}, {'w':7, 'v':11}, {'w':8, 'v':12},
                     {'w':8, 'v':13}, {'w':9, 'v':14}, {'w':9, 'v':15}, {'w':9, 'v':16}, {'w':10, 'v':16}, {'w':10, 'v':17}, {'w':11, 'v':18},
                     {'w': 12, 'v': 18}, {'w': 12, 'v': 19}, {'w': 13, 'v': 20}, {'w': 13, 'v': 21}, {'w': 14, 'v': 21}, {'w': 14, 'v': 22}])
treasureList.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':3, 'v':4}, {'w':3, 'v':5}, {'w':4, 'v':5}, {'w':4, 'v':6},
                     {'w':5, 'v':6}, {'w':5, 'v':7}, {'w':6, 'v':8}, {'w':6, 'v':9}, {'w':7, 'v':9}, {'w':7, 'v':10}, {'w':8, 'v':11},
                     {'w':8, 'v':12}, {'w':9, 'v':13}, {'w':9, 'v':14}, {'w':9, 'v':15}, {'w':10, 'v':16}, {'w':10, 'v':17}, {'w':11, 'v':18},
                     {'w': 11, 'v': 19}, {'w': 12, 'v': 20}, {'w': 13, 'v': 20}, {'w': 13, 'v': 21}, {'w': 14, 'v': 21}, {'w': 14, 'v': 22},
                     {'w': 14, 'v': 23}, {'w': 15, 'v': 24},{'w': 15, 'v': 25}, {'w': 16, 'v': 26},{'w': 17, 'v': 27}, {'w': 18, 'v': 28}])

maxWeightList = [20, 50, 80, 100, 150]
for i in range(len(treasureList)):
    maxValue, choosenList = dpMuseumThief(treasureList[i], maxWeightList[i])
    print(maxValue)
    print(choosenList)


6、代码三:使用递归和函数值缓存-需要用到全局变量MaxValueMap来进行函数值缓存

解释

  1. def MaxValue(i, w)返回值是在前i件宝物中,在不超过最大承重w的情况下取得的最大价值
  2. printChosenList(MaxValueMap,max_num, max_w)是将在前max_num件宝物中,在不超过最大承重max_w的情况下取得的最大价值时取得的宝物列表打印出来
  3. MaxValue(i, w) 函数用到了几个全局变量:
    MaxValueMap:是一个字典,key是(i,w),value是一个列表,列表中第一个元素是取得的最大价值,第二个元素是添加的宝物
    treasureList:是宝物列表,其中的宝物以字典形式存储。
  4. printChosenList(i, w) 函数用到的全局变量:
    MaxValueMap
  5. 使得宝物是从1开始编号的小技巧
    treasureList=[None]+treasure[k]
    但是要注意:在代码三的调用检验中,因为在for循环中已经使用treasureList作为一个全局变量并进行了赋值使用,因而原来的含有5组不同宝物列表的大列表更名为treasure
def MaxValue(i, w):
    if i==0 or w==0:
        MaxValueMap[(i,w)]=[0,None]
        return 0
    else:
        if treasureList[i]['w']>w:#装不下第i个
            #已经缓存过了
            if (i-1,w) in MaxValueMap:
            	MaxValueMap[(i,w)]=MaxValueMap[(i-1,w)]
                return MaxValueMap[(i-1,w)][0]
            #没有缓存过 
            return MaxValue(i-1,w)
        else:#装得下第i个
            if (i-1,w) in MaxValueMap:#已经缓存过了
                Value1=MaxValueMap[(i-1,w)][0]
            else:#没有缓存过 
                Value1=MaxValue(i-1,w)
            
            if (i-1,w-treasureList[i]['w']) in MaxValueMap:#已经缓存过了
                Value2=MaxValueMap[(i-1,w-treasureList[i]['w'])][0]+treasureList[i]['v']
            else:#没有缓存过 
                Value2=MaxValue(i-1,w-treasureList[i]['w'])+treasureList[i]['v']
            #比较,权衡
            if Value2>Value1:
                MaxValueMap[(i,w)]=[Value2,treasureList[i]]
                return Value2
            else:
                MaxValueMap[(i,w)]=[Value1,None]
                return Value1
def printChosenList(MaxValueMap,max_num, max_w):#回溯得到宝物
    i,w=max_num, max_w
    chosenList=[]
    while w>0:#由于MaxValueMap
        if (i,w)in MaxValueMap and MaxValueMap[(i,w)][1] is not None:
            chosenList.append(MaxValueMap[(i,w)][1])
            w=w-MaxValueMap[(i,w)][1]['w']
        i=i-1
    print(chosenList)

7、代码三的调用检验

# 检验
print("=========== 1 博物馆大盗问题 ============")
treasure = [[{'w':2, 'v':3}, {'w':3, 'v':4}, {'w':4, 'v':8}, {'w':5, 'v':8}, {'w':9, 'v':10}]]
treasure.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':4, 'v':5}, {'w':4, 'v':6}, {'w':4, 'v':7}, {'w':5, 'v':7},
                     {'w':5, 'v':8}, {'w':6, 'v':8}, {'w':6, 'v':10}, {'w':7, 'v':10}, {'w':7, 'v':12}, {'w':8, 'v':12}, {'w':8, 'v':13}, {'w':9, 'v':14}, {'w':9, 'v':16}])
treasure.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':3, 'v':4}, {'w':3, 'v':5}, {'w':4, 'v':6}, {'w':4, 'v':7},
                     {'w':5, 'v':7}, {'w':5, 'v':8}, {'w':6, 'v':8}, {'w':6, 'v':10}, {'w':7, 'v':11}, {'w':7, 'v':12}, {'w':8, 'v':13},
                     {'w':8, 'v':14}, {'w':9, 'v':15}, {'w':9, 'v':16}, {'w':9, 'v':17}, {'w':10, 'v':17}, {'w':10, 'v':18}, {'w':11, 'v':18}])
treasure.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':3, 'v':4}, {'w':3, 'v':5}, {'w':4, 'v':5}, {'w':4, 'v':6},
                     {'w':5, 'v':6}, {'w':5, 'v':7}, {'w':6, 'v':8}, {'w':6, 'v':9}, {'w':7, 'v':10}, {'w':7, 'v':11}, {'w':8, 'v':12},
                     {'w':8, 'v':13}, {'w':9, 'v':14}, {'w':9, 'v':15}, {'w':9, 'v':16}, {'w':10, 'v':16}, {'w':10, 'v':17}, {'w':11, 'v':18},
                     {'w': 12, 'v': 18}, {'w': 12, 'v': 19}, {'w': 13, 'v': 20}, {'w': 13, 'v': 21}, {'w': 14, 'v': 21}, {'w': 14, 'v': 22}])
treasure.append([{'w':1, 'v':2}, {'w':2, 'v':2}, {'w':2, 'v':3}, {'w':3, 'v':4}, {'w':3, 'v':5}, {'w':4, 'v':5}, {'w':4, 'v':6},
                     {'w':5, 'v':6}, {'w':5, 'v':7}, {'w':6, 'v':8}, {'w':6, 'v':9}, {'w':7, 'v':9}, {'w':7, 'v':10}, {'w':8, 'v':11},
                     {'w':8, 'v':12}, {'w':9, 'v':13}, {'w':9, 'v':14}, {'w':9, 'v':15}, {'w':10, 'v':16}, {'w':10, 'v':17}, {'w':11, 'v':18},
                     {'w': 11, 'v': 19}, {'w': 12, 'v': 20}, {'w': 13, 'v': 20}, {'w': 13, 'v': 21}, {'w': 14, 'v': 21}, {'w': 14, 'v': 22},
                     {'w': 14, 'v': 23}, {'w': 15, 'v': 24},{'w': 15, 'v': 25}, {'w': 16, 'v': 26},{'w': 17, 'v': 27}, {'w': 18, 'v': 28}])


    
maxWeightList = [20, 50, 80, 100, 150]
for k in range(len(treasure)):
    MaxValueMap={}
    treasureList=[None]+treasure[k]  
    max_w, max_num = maxWeightList[k], len(treasureList)-1
    print(MaxValue(max_num, max_w))
    printChosenList(MaxValueMap,max_num, max_w)

8、代码一、二、三的调用检验的正确结果

# 可有多种取法,以下只给出一种符合条件的宝物列表
# 29
# [{'w':2, 'v':3}, {'w':4, 'v':8}, {'w':5, 'v':8}, {'w':9, 'v':10}]
# 83
# [{'w': 1, 'v': 2}, {'w': 2, 'v': 3}, {'w': 4, 'v': 7}, {'w': 5, 'v': 8}, {'w': 6, 'v': 10}, {'w': 7, 'v': 12}, {'w': 8, 'v': 12}, {'w': 8, 'v': 13}, {'w': 9, 'v': 16}]
# 139
# [{'w': 1, 'v': 2}, {'w': 3, 'v': 5}, {'w': 4, 'v': 6}, {'w': 4, 'v': 7}, {'w': 6, 'v': 10}, {'w': 7, 'v': 12}, {'w': 8, 'v': 14}, {'w': 9, 'v': 15}, {'w': 9, 'v': 16}, {'w': 9, 'v': 17}, {'w': 10, 'v': 17}, {'w': 10, 'v': 18}]
# 164
# [{'w': 1, 'v': 2}, {'w': 3, 'v': 5}, {'w': 8, 'v': 13}, {'w': 9, 'v': 15}, {'w': 9, 'v': 16}, {'w': 10, 'v': 16}, {'w': 10, 'v': 17}, {'w': 11, 'v': 18}, {'w': 12, 'v': 19}, {'w': 13, 'v': 21}, {'w': 14, 'v': 22}]
# 246
# [{'w': 1, 'v': 2}, {'w': 3, 'v': 4}, {'w': 3, 'v': 5}, {'w': 9, 'v': 15}, {'w': 10, 'v': 17}, {'w': 11, 'v': 18}, {'w': 11, 'v': 19}, {'w': 12, 'v': 20}, {'w': 13, 'v': 21}, {'w': 14, 'v': 23}, {'w': 15, 'v': 24}, {'w': 15, 'v': 25}, {'w': 16, 'v': 26}, {'w': 17, 'v': 27}]

你可能感兴趣的:(数算Python版--习题总结)