动态规划(解析国王与挖矿问题)

动态规划包含三个重要概念:

  • 最优子结构
  • 边界 
  • 状态转移公式

 

例题1:爬楼梯问题 or 斐波拉契数列(单维度动态规划 相对容易)

例题2:国王和金矿 (双维度动态规划)

有一个国家发现了5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同。参与挖矿工人的总数是10人。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿。要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?

1、400金/5人   2、500金/5人   3、200金/3人    4、300金/4人    5、350金/3人

 

解析: N: 金矿数 ; M:人数; G[]:黄金量;   P[]:用工量;

            F(N,W):Solution函数。

            

            最优子结构:F( 4,10 ), F( 4,10-P[4] )+G[4] )

            解释:(前4个金矿10个工人的挖金数量)与(前4个金矿7个工人的挖金数量+第五个金矿的挖金数量)

            边界:当N=1,M>=P[0]   F( N,M ) =G[0]

                       当N=1,M< p[0] F(N,M)=0

            状态转移公式:F(5,10)=max( F( 4,10 ),F( 4,10-P[4] )+G[4] ) (N>1,M>=P[N-1])

                               or   F( N,M)=F( N-1,M )   (N>1,M<P[N-1])

            解释:5个金矿10个工人的挖金数量最优选择就是(前4个金矿10个工人的挖金数量)与(前4个金矿7个工人的挖金数量+第五个金矿的挖金数量)的较大值。(防止减出负数)

 

编程:

  • 自顶向下简单递归:时间空间:O(2^N) 与M无关。
class Solution1:
    def __init__(self,G=[],P=[]):
        self.G = G
        self.P = P
        self.N = len(self.G)
        
    def GoldMining(self,N,M): #金矿数量N,人数M; 黄金量列表G, 用工量列表P;
        # write code here
        #定义边界
        if N==1 and M>=self.P[0]:
            return self.G[0]
        if N==1 and 0<=M<self.P[0]:
            return 0
        #定义状态转移 两种情况
        if N>1 and M>=self.P[N-1]:
            return max( self.GoldMining(N-1,M),
                        self.GoldMining(N-1,M-self.P[N-1])+self.G[N-1] )
                       
        if N>1 and M<self.P[N-1]:
            return self.GoldMining(N-1,M) 
  • 备忘录递归:时间空间:O(N*M) (忽略hashMap操作的时间复杂度)。(略)
  • 自底向上:  时间:O(N*M), 空间:O(M) 。

 

class Solution2:
    def __init__(self,G=[],P=[]):
        self.G = G
        self.P = P
        self.N = len(self.G)
        
    def GoldMining(self,N,M): #金矿数量N,人数M; 黄金量列表G, 用工量列表P;
        # write code here
        preRes = [0]*M
        res = [0]*M
        #只有一个金矿的是时候 
        for i in range(M):
            if i+1<self.P[0]:
                preRes[i] = 0
            else:
                preRes[i] = self.G[0]
        res = preRes.copy()        
                
        for i in range(1,N):
        # 每一层代表前N个金矿人工数(1~10)人的解
        # 前一层已经求出相同人数时选取不同金矿的最大值所以可以叠加
            for j in range(M):
                if (j+1)<self.P[i]:   # j为坐标, j+1为人数
                    res[j] = preRes[j]
                else:
                    tempnum = 0 if j-self.P[i]<0 else j-self.P[i]
                    res[j] =max(preRes[j],preRes[tempnum]+self.G[i])    
            preRes = res.copy()     
        return res[-1]

 

扩展思路: 当挖掘工人总数非常大 且金矿数量比较小的时候:N:5, M:1000

自顶向下递归方法: 时间复杂度  O(2^N)=32 空间复杂度  O(2^5)=32 与M无关

自底向下迭代方法: 时间复杂度  O(M*N)=5000 空间复杂度 O(M)=1000

所以对于不同问题不总是迭代的时空复杂度优于递归。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TransientYear

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值