• 力扣刷题day34|62不同路径、63不同路径 II


    62. 不同路径

    力扣题目链接

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

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

    问总共有多少条不同的路径?

    示例 1:

    输入:m = 3, n = 7
    输出:28
    
    • 1
    • 2

    示例 2:

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

    示例 3:

    输入:m = 7, n = 3
    输出:28
    
    • 1
    • 2

    示例 4:

    输入:m = 3, n = 3
    输出:6
    
    • 1
    • 2

    思路

    机器人从(0 , 0) 位置出发,到(m - 1, n - 1)终点。

    动态规划五部曲
    1. 确定dp数组以及下标的含义

    dp[i] [j] :表示从(0 ,0)出发,到(i, j) 有dp[i] [j]条不同的路径。

    1. 确定递推公式

    因为上一步只能向右或者向下走到达这一步的位置,想要求dp[i] [j],只能有两个方向来推导出来,即dp[i - 1] [j] 和 dp[i] [j - 1]。

    而dp[i - 1] [j] 又表示从(0, 0)的位置到(i - 1, j)有几条路径

    dp[i] [j - 1] 又表示从(0, 0)的位置到(i, j - 1)有几条路径

    所以dp[i] [j] = dp[i - 1] [j] + dp[i] [j - 1],因为dp[i][j]只有这两个方向过来。

    1. dp数组的初始化

    首先dp[i] [0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0] [j]也同理。

    for (int i = 0; i < m; i++) dp[i][0] = 1;
    for (int j = 0; j < n; j++) dp[0][j] = 1;
    
    • 1
    • 2
    1. 确定遍历顺序

    这里要看一下递归公式dp[i] [j] = dp[i - 1] [j] + dp[i] [j - 1],dp[i] [j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。

    这样就可以保证推导dp[i] [j]的时候,dp[i - 1] [j] 和 dp[i] [j - 1]一定是有数值的。

    1. 举例推导dp数组

    image-20221029203203248

    完整代码

    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        // 初始化
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n; j++) {
            dp[0][j] = 1;
        }
    
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
    
        return dp[m - 1][n - 1];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    63. 不同路径 II

    力扣题目链接

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

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

    现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

    网格中的障碍物和空位置分别用 10 来表示。

    示例 1:

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

    示例 2:

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

    思路

    有障碍就表示没有路径能到这一点,即这一点dp[i] [j]为0。

    动态规划五部曲
    1. 确定dp数组以及下标的含义

    dp[i] [j] :表示从(0 ,0)出发,到(i, j) 有dp[i] [j]条不同的路径。

    如果(0 ,0)就有障碍,那么一开始就走不下去

    1. 确定递推公式

    递推公式和62.不同路径一样,dp[i] [j] = dp[i - 1] [j] + dp[i] [j - 1]。

    但这里需要注意一点,因为有了障碍(obstacleGrid为1),(i, j)如果就是障碍的话应该就保持初始状态(初始状态为0)

    if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    }
    
    • 1
    • 2
    • 3
    1. dp数组如何初始化

    从(0, 0)的位置到(i, 0)的路径只有一条,所以dp[i][0]一定为1,dp[0][j]也同理。

    但如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后的dp[i][0]应该还是初始值0。

    image-20221029212013252

    下标(0, j)的初始化情况同理。

    // 初始化,碰到障碍后面就都去不了
    for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
        dp[i][0] = 1;
    }
    for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
        dp[0][j] = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 确定遍历顺序

    递归公式dp[i] [j] = dp[i - 1] [j] + dp[i] [j - 1],dp[i] [j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。

    这样就可以保证推导dp[i] [j]的时候,dp[i - 1] [j] 和 dp[i] [j - 1]一定是有数值的。

    1. 举例推导dp数组

    由示例1得

    image-20221029212852424

    完整代码

    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
    
        //如果在起点或终点出现了障碍,直接返回0
        if (obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1) {
            return 0;
        }
    
        // 初始化,碰到障碍后面就都去不了
        for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[0][j] = 1;
        }
    
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (obstacleGrid[i][j] == 1) continue;
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    vue路由及其页面
    一文拿捏线程和线程池的创建方式
    <b><strong>,<i><em>标签的区别
    【Flask基础】八,Flask数据库操作(增删改查)
    bean 的六种作用域
    jvm深入研究文档--整体概念
    企业应用现代化实用教程 | 如何快、准、狠地进行应用容器化改造?
    【ES实战】ES创建Transports客户端时间过长分析
    七夕情人节表白网页代码大全(浪漫的html表白源代码)
    VS编译.cu文件源文件无法打开matrix.h和mex.h问题
  • 原文地址:https://blog.csdn.net/dtc1261/article/details/127592757