Python三维动态规划(3D Dynamic Programming)

目录

前言:

一、一维DP

二、二维DP

①求方案数

Ⅰ、数学方法

Ⅱ、DP

②求最小值

三、三维DP

①求方案数

​ Ⅰ、数学方法

 Ⅱ、DP

②求最小值

四、关于三维DP步骤的解释

 五、总结


前言:    

      动态规划的适用条件为最优化原理和无后效性,可以有效地利用历史记录避免重复计算。动态规划一般由三个步骤组成:①定义数组;②求解初始值;③利用状态转移方程求解最优策略。(部分内容参考告别动态规划,连刷40道动规算法题,我总结了动规的套路

      为更好的理解三维DP问题的解法,从一维和二维开始介绍。

一、一维DP

问题1

描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

def g11(n):
    if n<=2:
        return n
    else:
        dp=[0 for i in range(n)] #创建n*1的列表
        dp[0]=1;dp[1]=2          #初始值
        for i in range(2,n):
            dp[i]=dp[i-1]+dp[i-2]#递归式:状态转移方程
        return dp[n-1]
>>> g11(10)
89

二、二维DP

①求方案数

问题2.1

描述:在m*n的方格纸上,一只蚂蚁从左上角(1,1)移动到右下角(m,n),只能往下或者往右移动,每次移动一格,有多少种方案?本题以5*6为例

Ⅰ、数学方法

C^q_p,其中p=m+n-2,q=m-1

#定义组合函数c(n,m)=n!/m!(n-m)!
#阶层函数:factorial
from math import factorial as f
def c(n,m):
    r=int(f(n)/f(m)/f(n-m))
    return r

>>> c(9,4)
126

Ⅱ、DP

def g21(m,n):
    if m==1 or n==1: #显然当列或行为1时,方案唯一
        return 1
    else:
        dp=[[0 for i in range(n)] for j in range(m)] #创建m*n列表用于储存结果

        #初始值
        for i in range(m): #第一列方案都是1
            dp[i][0]=1
        for j in range(n): #第一行方案都是1
            dp[0][j]=1

        #状态转移方程
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j]=dp[i-1][j]+dp[i][j-1]
        
        return dp[m-1][n-1]

>>> g21(5,6)
126

②求最小值

问题2.2

描述:在m*n方格纸上,每个格点(行列交叉处)上有未知个数的水滴,蚂蚁从左上角到右下角沾的水滴总数最小是多少? l 为传入的列表,记录了对应点位置的水滴数,例如

l=[[1,2,3]] 对应1*3

l=[[1],[2],[3]] 对应3*1

l=[[1,2],[3,4]] 对应2*2

def g22(l):
    [m,n]=[len(l),len(l[0])]  #记录l的行列信息

    #特殊情况
    if m==1:
        return sum(l[0])
    elif n==1:
        return sum([i[0] for i in l]) 

    else:
        #创建m*n全0列表
        dp=[[0 for i in range(n)] for j in range(m)]

        #第一行和第一列作为初始值
        for i in range(m):
            dp[i][0]=dp[i-1][0]+l[i][0]
        for j in range(n):
            dp[0][j]=dp[0][j-1]+l[0][j]

        #状态转移方程
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+l[i][j]

        return dp[m-1][n-1]

>>> g22([[1],[2],[3]])
6
>>> g22([[1,2,3]])
6
>>> g22([[1,2,3],[4,5,6]]) #1+2+3+6=12
12

三、三维DP

①求方案数

问题3.1

描述:在m*n*p的立方体中,以整数点为格点,蚂蚁每次移动1个单位长度,方向如图所示,从起点到终点共有多少种方案? 本题以3*4*5为例

Ⅰ、数学方法

C^q_k*C^r_s,其中,k=m+n+p-3,q=m-1,s=k-q=n+p-2,r=n-1

from math import factorial as f
def c(n,m):
    r=int(f(n)/f(m)/f(n-m))
    return r

>>> c(9,2)*c(7,3)
1260

 Ⅱ、DP

def g31(m,n,p):
    l=[m,n,p]
    
    #特殊情况降维
    if 1 in l:
        l.remove(1)
        return g21(l[0],l[1])

    else:
        #创建m*n*p的列表
        dp=[[[0 for i in range(p)] for j in range(n)] for k in range(m)]

        #初值需计算三个平面,mon,mop,nop,o是原点
        #平面内即可降维
        for k in range(m):
            for j in range(n):
                for i in range(p):
                    l=[k,j,i]
                    if 0 in [k,j,i]:
                        l.remove(0)
                        dp[k][j][i]=g21(l[0]+1,l[1]+1)

        #状态转移方程
        for k in range(1,m):
            for j in range(1,n):
                for i in range(1,p):
                    dp[k][j][i]=dp[k][j][i-1]+dp[k][j-1][i]+dp[k-1][j][i]

        return dp[m-1][n-1][p-1]

>>> g31(3,4,5)
1260

②求最小值

问题3.2

描述:在m*n*p的立方体中,以整数点为格点,蚂蚁每次移动1个单位长度,方向如图所示,

每个格点都有一定的水滴,问蚂蚁从起点到终点最少沾的水滴数?l为传入的列表,记录了对应位置的水滴数,例如

l=[[[1,2],[3,4]]] 对应 1*2*2

l=[[[1,2]],[[3,4]]] 对应 2*1*2

l=[[[1],[2]],[[3],[4]]]对应 2*2*1

l=[[[1,2],[3,4]],[[1,2],[3,4]]] 对应 2*2*2

def g32(l):
    l1=[m,n,p]=[len(l),len(l[0]),len(l[0][0])]

    #创立m*n*p全0列表
    dp=[[[0 for i in range(p)] for j in range(n)] for k in range(m)]

    #特殊情况,降维
    if 1 in l1:
        a=l1.index(1)
        del l1[a]
        l2=[[0 for i in range(l1[0])] for j in range(l1[1])]

        if a==0:
            for j in range(n):
                for i in range(p):
                    print(i,j)
                    l2[j][i]=l[0][j][i]

        elif a==1:
            for k in range(m):
                for i in range(p):
                    l2[k][i]=l[k][0][i]

        else:
            for k in range(m):
                for j in range(n):
                    l2[k][j]=l[k][j][0]

        return g22(l2)
    else:
        
        #初始值1,三条轴上的赋值
        for k in range(m):
            dp[k][0][0]=dp[k-1][0][0]+l[k][0][0]
        for j in range(n):
            dp[0][j][0]=dp[0][j-1][0]+l[0][j][0]
            print(dp[0][j][0])
        for i in range(p):
            dp[0][0][i]=dp[0][0][i-1]+l[0][0][i]

        #初始值2,三个平面的赋值
        #mon平面
        for k in range(1,m):
            for j in range(1,n):
                dp[k][j][0]=min(dp[k-1][j][0],dp[k][j-1][0])+l[k][j][0]

        #nop平面
        for j in range(1,n):
            for i in range(1,p):
                dp[0][j][i]=min(dp[0][j-1][i],dp[0][j][i-1])+l[0][j][i]

        #mop平面
        for k in range(1,m):
            for i in range(1,p):
                dp[k][0][i]=min(dp[k-1][0][i],dp[k][0][i-1])+l[k][0][i]

        #状态转移方程
        for k in range(1,m):
            for j in range(1,n):
                for i in range(1,p):
                    dp[k][j][i]=min(dp[k][j][i-1],dp[k][j-1][i],dp[k-1][j][i])+l[k][j][i]

        return dp[m-1][n-1][p-1]

测试:

>>> g32([[[1,2],[3,4]]] ) #1*2*2
7
>>> g32([[[1,2]],[[3,4]]] ) #2*1*2
7
>>> g32([[[1],[2]],[[3],[4]]]) #2*2*1
7
>>> g32([[[1,2],[3,4]],[[1,2],[3,4]]]) #2*2*2
8

四、关于三维DP步骤的解释

以上面传入列表2*2*2 为例说明

①先将传入的数据在正方体上标好

②初始值1,将三条轴上的数据计算出来 

③初始值2,将三个平面上的点对应数据计算出来 

④利用状态转移方程计算 

 五、总结

        首先,创建相应维度的列表是为了储存到每一点对应题目要求的解,因为要用到递归(状态转移,例如从n--->n-1,n-2),把途经的每一步记录下来减少重复计算,比如说三维DP中dp[k][j][i]对应从原点到该点(k,j,i)的最少水滴数

        对于初始值:递归到0,1等时无法用递归式计算,比如-1没有意义,所以需要给定初值

        其实,从三维DP的代码就能看出,维数每+1,题目的难度至少上升一个级别,例如,对于四维DP m*p*q*r,给定的初值条件会更多,当维度继续上升时,难度可想而知,这也是DP的一个缺陷,即“维数障碍”

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿白啥也不会

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

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

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

打赏作者

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

抵扣说明:

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

余额充值