动态规划 (算法图解笔记4)

从小偷背包问题出发

假如你是个小偷,背着一个可装四磅的背包,你可偷的商品如下, 为了让盗窃的商品价值更高,你该选择那些商品。
在这里插入图片描述

最简单的解法,列举出所有的商品组合,然后选择最优的方案 。但是这是一个np完全问题, 算法复杂度为O(2^n) 。商品数目少的时候可以,商品数目多的时候问题变得不可解。

动态规划的思想,就是将一个棘手的问题转化为小问题,然后不断扩大问题的规模 。即先解决子问题,再解决大问题。

确定上面的思路之后,事情就简单了, 那么对小偷背包问题,最简单的情况是什么呢?
肯定是不偷,不偷就不要选择 。
然后就是只有一件商品可以偷,比如吉他,
当小偷的背包只能装一磅时,那么最优的选择就是选择偷吉他。价值1500
当小偷的背包能装两磅时,那么最优的选择就是选择偷吉他。价值1500
当小偷的背包能装三磅时,那么最优的选择就是选择偷吉他。价值1500
当小偷的背包能装四磅时,那么最优的选择就是选择偷吉他。因为此时只有一个商品吉他可以偷 。价值1500。

可偷物品1234
吉他1500150015001500

当小偷可偷吉他和音响时,
小偷的背包只能装一磅时,那么最优的选择就是选择偷吉他。
当小偷的背包能装两磅时,那么最优的选择就是选择偷吉他。
当小偷的背包能装三磅时,那么最优的选择就是选择偷吉他。
当小偷的背包能装四磅时,那么最优的选择就是选择偷音响。

可偷物品1234
吉他1500150015001500
吉他+音响1500150015003000

关键点在于,当多了一件商品时,情况变的复杂了。但这时候我们只需要选择要不要偷这件商品 ?
只需要比较偷和不偷哪个获得的价值更。如何比较呢?
不偷,问题的最优解就是之前考虑的没有音响的小问题 。那么选择不偷的最优解就是只能偷吉他的最优解。
偷,小偷获得价值就是 音响的价值 加上背包剩下的空间的最优解的价值。 而剩下空间的最优解之前小问题都已经分析过了。

举个具体的例子, 当现在背包为4时,
是否偷音响,不偷,最优的价值就是1500。偷,就是音响的价值+背包剩下空间的价值( 0)= 3000。 那么偷更划算 。那就偷 。

之后的每一步都是这样 , 多新增一个物品时,只需要比较偷和不偷的价值哪个更优,得出当前最优解 。极大的简化了问题。

可偷物品1234
吉他1500150015001500
吉他+音响1500150015003000
吉他+音响+电脑1500150020003500

当背包三磅时,不偷电脑,最优价值1500 。偷,电脑价值(2000) + 剩余空间(0)的最优价值(0) = 2000 .所以偷电脑 。
当背包四磅时,不偷电脑,最优价值3000 。偷,电脑价值(2000) + 剩余空间(1)的最优价值(1500) = 3500 .所以偷电脑 。

逐渐增加更多是的商品也是如此,每一步只要简单的判断。极大的降低了算法的复杂性 。
与分而治之不同的是,动态规划中我们需要思考怎么让小问题更大 ,逐渐接近最终的问题 。

代码实现

小偷背包问题本质上就是这样一个模型, 从一个集合中选择一个子集 ,每个元素都有其价值和价格 。如何在有限的成本中选出价值最高的子集 。

import numpy as np
    
def dynamic_planning(dic, Money):
    data = np.zeros([len(dic),len(Money)])
    for item_th in range(1,len(dic)):
        for i, money in enumerate(Money):
            cost= dic[item_th][1]; value = dic[item_th][2]
            #print(item_th, money)
            if cost <= money :
                join_value = value+data[item_th-1, Money.index(money-cost)] 
            else: join_value = 0
            # print(join_value)
            if data[item_th-1,i] <= join_value:
                data[item_th, i] = join_value
            else : data[item_th, i] = data[item_th-1,i]
    print(data)
    #-----
    plan=[data[-1,-1]]
    i, j = data.shape[0]-1,data.shape[1]-1
    while j!=0 and i!=0:
        if  data[i,j] > data[i-1,j]:
            #print(i,dic)#,dic[i][0])
            plan.append(dic[i][0])
            j = Money.index(Money[j] - dic[i][1])  
            i = i-1 
        else: 
            i = i-1
    plan.reverse()
    return plan

我该偷哪些商品?

dic_thief = { 0:['name','cost','value'],
       1:['guita',1, 1500], 
       2:[ 'sound',4,3000],
       3:['pc', 3,2000],
       4:['iphone',1,2000]}   
Money = list(range(5))
plan = dynamic_planning(dic_thief, Money)
[[   0.    0.    0.    0.    0.]
 [   0. 1500. 1500. 1500. 1500.]
 [   0. 1500. 1500. 1500. 3000.]
 [   0. 1500. 1500. 2000. 3500.]
 [   0. 2000. 3500. 3500. 4000.]]

Out: ['pc', 'iphone', 4000.0]

解决类似问题

英国旅行去那些地方?
在这里插入图片描述

dic_trip = {0 :['scenic', 'cost','value'],
            1:['高斯敏斯特教堂', 0.5,7],
            2:['环球剧场', 0.5, 6],
            3:['英国国家美术馆', 1, 9],
            4:['大英博物馆', 2, 9],
            5:['圣保罗大教堂',0.5,8]} 
Money_trip= [0,.5, 1, 1.5,2]
plan  = dynamic_planning(dic=dic_trip,Money=Money_trip)
[[ 0.  0.  0.  0.  0.]
 [ 0.  7.  7.  7.  7.]
 [ 0.  7. 13. 13. 13.]
 [ 0.  7. 13. 16. 22.]
 [ 0.  7. 13. 16. 22.]
 [ 0.  8. 15. 21. 24.]]

Out: ['高斯敏斯特教堂', '英国国家美术馆', '圣保罗大教堂', 24.0]

旅行带哪些物品?
在这里插入图片描述

dic_trip = {0 :['scenic', 'cost','value'],
            1:['水', 3,10],
            2:['书', 1, 3],
            3:['食物', 2, 9],
            4:['夹克', 2, 5],
            5:['相机',1,6]} 
Money_trip= list(range(7))

plan  = dynamic_planning(dic=dic_trip,Money=Money_trip)
[[ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0. 10. 10. 10. 10.]
 [ 0.  3.  3. 10. 13. 13. 13.]
 [ 0.  3.  9. 12. 13. 19. 22.]
 [ 0.  3.  9. 12. 14. 19. 22.]
 [ 0.  6.  9. 15. 18. 20. 25.]]

Out: ['水', '食物', '相机', 25.0]
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值