Leetcode刷题笔记39:动态规划2(62.不同路径-63. 不同路径 II)

95 阅读3分钟

导语

leetcode刷题笔记记录,本篇博客是动态规划部分的第二期,主要记录题目包括:

  • 62.不同路径
    1. 不同路径 II

Leetcode 62.不同路径

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?

示例 1:

输入: m = 3, n = 7
输出: 28

示例 2:

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

解法

使用动规五部曲:

  1. dp数组含义:dp[i][j]表示从(0,0)到(i,j)有多少种不同的路径;
  2. 递推公式:
dp[i][j]=dp[i1][j]+dp[i][j1]dp[i][j] = dp[i-1][j] + dp[i][j-1]
  1. 初始化:应该将第一行和第一列初始化为1
  2. 遍历顺序:由于递归公式中需要上方和左方的值,所以应该是从上往下,从左往右;
  3. 打印dp数组;
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # 初始化一个 m x n 的二维数组,全部填充为0
        # 这个数组dp[i][j]表示从起始点到(i, j)位置的不同路径数量
        dp = [[0] * n for i in range(m)]
        
        # 设置起点到第一列的任意位置都只有一条路径(机器人只能一直向下走)
        for i in range(m):
            dp[i][0] = 1
        
        # 设置起点到第一行的任意位置都只有一条路径(机器人只能一直向右走)
        for j in range(n):
            dp[0][j] = 1
        
        # 使用动态规划填充数组
        # 对于位置(i, j),机器人只有两种方式到达:从(i-1, j) 或者 从(i, j-1)
        # 所以,dp[i][j] = dp[i-1][j] + dp[i][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]

        # 返回起始点到(m-1, n-1)位置的路径数量,即整个网格的右下角
        return dp[m-1][n-1]

易错点

遍历时,i,j都是从1开始的,而不是0,请注意!

Leetcode 63. 不同路径 II

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

输入: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出: 2
解释: 3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

输入: obstacleGrid = [[0,1],[0,0]]
输出: 1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] 为 0 或 1

解法

相比于上一题,这一题增加了障碍,因此,我们的递推公式和数组初始化部分需要进行修改,具体如下:

  1. 递归公式的更新仅当该位置没有障碍时才进行更新;
  2. dp数组初始化时,遇到障碍后的地方初始化为0,而不是1;

修改后代码如下:

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        # 获取网格的行数m和列数n
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        
        # 初始化一个 m x n 的二维数组,全部填充为0
        # 这个数组dp[i][j]表示从起始点到(i, j)位置的不同路径数量
        dp = [[0] * n for i in range(m)]
        
        # 初始化第一列的路径数量
        # 如果一个位置上有障碍物,则这个位置及其之后的所有位置都不可达,因此路径数量为0
        for i in range(m):
            if obstacleGrid[i][0] == 1:
                break
            dp[i][0] = 1
        
        # 初始化第一行的路径数量
        # 如果一个位置上有障碍物,则这个位置及其之后的所有位置都不可达,因此路径数量为0
        for j in range(n):
            if obstacleGrid[0][j] == 1:
                break
            dp[0][j] = 1
        
        # 使用动态规划填充数组
        # 对于没有障碍物的位置(i, j),机器人只有两种方式到达:从(i-1, j) 或者 从(i, j-1)
        # 所以,dp[i][j] = dp[i-1][j] + dp[i][j-1]
        # 如果位置(i, j)上有障碍物,则 dp[i][j] 默认为0
        for i in range(1, m):
            for j in range(1, n):
                if obstacleGrid[i][j] == 0:
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]

        # 返回起始点到(m-1, n-1)位置的路径数量,即整个网格的右下角
        return dp[m-1][n-1]

总结

image.png