动态规划1

动态规划
1)、定义数组元素的含义
2)、找出数组元素间的关系式
3)、找出初始条件

问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
(可以从最后的阶段来假设,比如有10层台阶,青蛙要怎么跳,可以直接从第9层直接到达,也可以从第8层跳到达所以有f(10)=f(9)+f(8))

def f(n):
    dp=[0 for i in range(n+1)]
    dp[0]=0
    dp[1]=1
    dp[2]=2
    for i in range(3,(n+1)):
        dp[i]=dp[i-1]+dp[i-2]
    return dp[n]

时间复杂度为2的n次方。
使用动态规划时:
n为1和2时是显而易见的,当n=3时,可通过1和2的结果来得出,当n=4时,可通过2和3的结果来得出,我们只需要保存最近的两个结果就可以了。所以,还可以进一步优化。

def dp(n):
    if(n<=2):
        return n
    a=1
    b=2
    temp=0
    for i in range(3,(n+1)):
        temp=a+b
        a=b
        b=temp
    return temp

案例二:二维数组的 DP
问题描述
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?在这里插入图片描述
步骤一、定义数组元素的含义
由于我们的目的是从左上角到右下角一共有多少种路径,那我们就定义 dp[i] [j]的含义为:当机器人从左上角走到(i, j) 这个位置时,一共有 dp[i] [j] 种路径。那么,dp[m-1] [n-1] 就是我们要的答案了。

注意,这个网格相当于一个二维数组,数组是从下标为 0 开始算起的,所以 右下角的位置是 (m-1, n - 1),所以 dp[m-1] [n-1] 就是我们要找的答案。
步骤二:找出关系数组元素间的关系式
想象以下,机器人要怎么样才能到达 (i, j) 这个位置?由于机器人可以向下走或者向右走,所以有两种方式到达

一种是从 (i-1, j) 这个位置走一步到达

一种是从(i, j - 1) 这个位置走一步到达

因为是计算所有可能的步骤,所以是把所有可能走的路径都加起来,所以关系式是 dp[i] [j] = dp[i-1] [j] + dp[i] [j-1]。

步骤三、找出初始值
显然,当 dp[i] [j] 中,如果 i 或者 j 有一个为 0,那么还能使用关系式吗?答是不能的,因为这个时候把 i - 1 或者 j - 1,就变成负数了,数组就会出问题了,所以我们的初始值是计算出所有的 dp[0] [0….n-1] 和所有的 dp[0….m-1] [0]。这个还是非常容易计算的,相当于计算机图中的最上面一行和左边一列。因此初始值如下:

dp[0] [0….n-1] = 1; // 相当于最上面一行,机器人只能一直往左走

dp[0…m-1] [0] = 1; // 相当于最左面一列,机器人只能一直往下走

def uniquePaths(m,n):
    if (m <= 0 | n <= 0):
        return 0
    dp=[[0 for i in range(n)] for i in range(m)]
    #初始化
    for i in range(0,m):
        dp[i][0]=1
    for j in range(0,n):
        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]

O(n*m) 的空间复杂度
优化:二维变一维
然后根据公式 dp[i][j] = dp[i-1][j] + dp[i][j-1] 来填充矩阵的其他值。下面我们先填充第二行的值。
在这里插入图片描述
大家想一个问题,当我们要填充第三行的值的时候,我们需要用到第一行的值吗?答是不需要的,不行你试试,当你要填充第三,第四…第 n 行的时候,第一行的值永远不会用到,只要填充第二行的值时会用到。

根据公式 dp[i][j] = dp[i-1][j] + dp[i][j-1],我们可以知道,当我们要计算第 i 行的值时,除了会用到第 i - 1 行外,其他第 1 至 第 i-2 行的值我们都是不需要用到的,也就是说,对于那部分用不到的值我们还有必要保存他们吗?

答是没必要,我们只需要用一个一维的 dp[] 来保存一行的历史记录就可以了。然后在计算机的过程中,不断着更新 dp[] 的值在这里插入图片描述
此时,dp[i] 将完全保存着第二行的值,并且我们可以推导出公式

dp[i] = dp[i-1] + dp[i]

dp[i-1] 相当于之前的 dp[i-1][j],dp[i] 相当于之前的 dp[i][j-1]。
于是按照这个公式不停着填充到最后一行
在这里插入图片描述
最后 dp[n-1] 就是我们要求的结果了。

def uniquePaths(m,n):
    if m <= 0 | n <= 0:
        return 0
    dp=[1 for i in range(n)]
    for i in range(m-1):#因为之前已经为第一行赋好了值,所以此处可以少一行
        for j in range(n):
            dp[j]=dp[j-1]+dp[j]
            dp[0] = 1 #注意dp[0]应该放在后面
    return dp[n-1]

有时候自己画一下图会更好理解
在这里插入图片描述
问题描述
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。
举例:
输入:
arr = [
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

步骤一、定义数组元素的含义
由于我们的目的是从左上角到右下角,最小路径和是多少,那我们就定义 dp[i] [j]的含义为:当机器人从左上角走到(i, j) 这个位置时,最下的路径和是 dp[i] [j]。那么,dp[m-1] [n-1] 就是我们要的答案了。

注意,这个网格相当于一个二维数组,数组是从下标为 0 开始算起的,所以 由下角的位置是 (m-1, n - 1),所以 dp[m-1] [n-1] 就是我们要走的答案。
步骤二:找出关系数组元素间的关系式
想象以下,机器人要怎么样才能到达 (i, j) 这个位置?由于机器人可以向下走或者向右走,所以有两种方式到达

一种是从 (i-1, j) 这个位置走一步到达

一种是从(i, j - 1) 这个位置走一步到达

不过这次不是计算所有可能路径,而是计算哪一个路径和是最小的,那么我们要从这两种方式中,选择一种,使得dp[i] [j] 的值是最小的,显然有

dp[i] [j] = min(dp[i-1][j],dp[i][j-1]) + arr[i][j];// arr[i][j] 表示网格种的值
步骤三、找出初始值
显然,当 dp[i] [j] 中,如果 i 或者 j 有一个为 0,那么还能使用关系式吗?答是不能的,因为这个时候把 i - 1 或者 j - 1,就变成负数了,数组就会出问题了,所以我们的初始值是计算出所有的 dp[0] [0….n-1] 和所有的 dp[0….m-1] [0]。这个还是非常容易计算的,相当于计算机图中的最上面一行和左边一列。因此初始值如下:

dp[0] [j] = arr[0] [j] + dp[0] [j-1]; // 相当于最上面一行,机器人只能一直往左走

dp[i] [0] = arr[i] [0] + dp[i] [0]; // 相当于最左面一列,机器人只能一直往下走

def uniquePathes(grid):
    m=len(grid)
    n=len(grid[0])
    if (m<0|n<0):
        return 0
    dp = [[0 for i in range(n)] for i in range(m)]
    #初始化
    dp[0][0]=grid[0][0]
    for i in range(1,n):
        dp[0][i]=dp[0][i-1]+grid[0][i]
    for j in range(1,m):
        dp[j][0]=dp[j-1][0]+grid[j][0]
    #关系式
    for i in range(1,m):
        for j in range(1,n):
            dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]
    return dp[m-1][n-1]

优化----二维数组变一维数组

def uniquePathes(grid):
    m=len(grid)
    n=len(grid[0])
    if (m<0|n<0):
        return 0
    dp = [0 for i in range(n)]
    #初始化
    dp[0]=grid[0][0]
    for i in range(1,n):
        dp[i]=dp[i-1]+grid[0][i]
    for i in range(1,m):
        dp[0] = dp[0] + grid[i][0]
        for j in range(1,n):
            dp[j]=min(dp[j-1],dp[j])+grid[i][j]
    return dp[n-1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值