• 代码随想录39——动态规划:62不同路径、63不同路径II


    1.62不同路径

    参考:代码随想录,62不同路径力扣题目链接

    1.1.题目

    在这里插入图片描述
    在这里插入图片描述

    1.2.解答

    机器人从(0 , 0) 位置出发,到(m - 1, n - 1)终点。按照动规五部曲来分析:

    1.确定dp数组(dp table)以及下标的含义

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

    2.确定递推公式

    想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j]dp[i][j - 1]

    此时在回顾一下 dp[i - 1][j] 表示啥,是从(0, 0)的位置到(i - 1, j)有几条路径;dp[i][j - 1]同理。

    那么很自然,dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个方向过来。

    3.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

    4.确定遍历顺序

    这里要看一下递归公式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]一定是有数值的。

    5.举例推导dp数组

    如图所示:

    在这里插入图片描述
    代码:

    int uniquePaths(int m, int n)
    {
        // 1.构造dp数组
        vector<vector<int>> dp(m, vector<int>(n, 0));   
    
        // 2.初始化dp数组
        for(int i = 0; i < m; i++)
            dp[i][0] = 1;
        for(int j = 0; j < n; j++)
            dp[0][j] = 1;
    
        // 3.从上到下、从左到右遍历dp数组
        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

    2.63不同路径II

    参考:代码随想录,63不同路径II力扣题目链接

    2.1.题目

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    2.2.解答

    这道题相对于62.不同路径就是有了障碍。

    第一次接触这种题目的同学可能会有点懵,这有障碍了,应该怎么算呢?

    62.不同路径中我们已经详细分析了没有障碍的情况,有障碍的话,其实就是标记对应的dp table(dp数组)保持初始值(0)就可以了

    注意:障碍物的地方dp数组保持是0的话,对计算到达当前障碍物的位置有几条路径来说是没有意义的。而是对于计算下一个位置的时候,比如当前障碍物的下一行对应位置,那么肯定不能从当前障碍物位置到达了,但是代码中我们还是要相加障碍物这个位置的结果,所以让它保持是0就没问题了。

    动规五部曲

    1.确定dp数组(dp table)以及下标的含义

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

    2.确定递推公式

    递推公式和62.不同路径一样,dp[i][j] = dp[i - 1][j] + dp[i][j - 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

    3.dp数组如何初始化

    在62.不同路径不同路径中我们给出如下的初始化:

    vector<vector<int>> dp(m, vector<int>(n, 0)); // 初始值为0
    for (int i = 0; i < m; i++) dp[i][0] = 1;
    for (int j = 0; j < n; j++) dp[0][j] = 1;
    
    • 1
    • 2
    • 3

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

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

    在这里插入图片描述
    下标(0, j)的初始化情况同理。

    所以本题初始化代码为:

    vector<vector<int>> dp(m, vector<int>(n, 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;
    
    • 1
    • 2
    • 3

    注意代码里for循环的终止条件,一旦遇到obstacleGrid[i][0] == 1的情况就停止dp[i][0]的赋值1的操作,dp[0][j]同理。

    4.确定遍历顺序

    从递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 中可以看出,一定是从左到右一层一层遍历,这样保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][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];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.举例推导dp数组
    这里就不举例了。

    最后给出代码如下,非常简单:

    int uniquePathsWithObstacles(vector<vector<int>> &obstacleGrid)
    {
        // 1.定义dp数组
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
    
        // 2.初始化dp数组,注意一旦遇到障碍物,直接不满足for循环,for循环就退出了,然后dp就保持原来的数值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;
        
        // 3.从上到下,从左到右遍历每一个位置
        for(int i = 1; i < m; i++)
            for(int j = 1; j < n; j++) 
                // 注意:当前位置是空地才计算路径条数,如果是障碍物,则直接保持原来的dp是0的数值
                if(obstacleGrid[i][j] == 0)   
                    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
  • 相关阅读:
    数据分析之AB测试
    刷题记录(NC50959 To the Max,NC236172 货船,NC16655 [NOIP2005]过河)
    TODOLIST
    金仓数据库KingbaseES客户端编程接口指南-Perl DBI(3. DBI 类)
    学生个人网页设计作品:旅游网页设计与实现——成都旅游网站4个页HTML+CSS web前端网页设计期末课程大作业 学生DW静态网页设计 学生个人网页设计作品
    Android简易音乐重构MVVM Java版-新增歌曲播放界面+状态栏黑科技(十七)
    SystemV
    奉劝不要在ElementPlus中使用el-table渲染大量树形结构数据
    Android Gradle 构建脚本中使用了不安全的协议
    如何知道当前ubuntu的版本
  • 原文地址:https://blog.csdn.net/qq_42731705/article/details/127589867