LeetCode题解 动态规划(二):62 不同路径;63 不同路径II

62 不同路径 medium

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

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

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

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ofNipZHZ-1675774884935)(https://farewellyi-pic-bed.oss-cn-hangzhou.aliyuncs.com/img/202302072015073.png)]

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

经典的动态规划题目,强化学习入门的一个环境与此题相同。

还是五部曲:

1 - 确定dp数组:这道题与此前的不同,此前是一维的,这道题则是二维。先用二维的思路解决。

那么dp[i] [j]就表示机器人移动到第 i 行,第 j 列时,路径的数量。

2 - 确定递推公式:由于机器人只能向下或者向右移动,那么移动到第 i 行,第 j 列,就只有两种可能,一是从第 i - 1 行,第 j 列而来(向下移动),二是第 i 行,第 j - 1 列而来(向右移动)。那么dp[i] [j] = dp[i - 1] [j] + dp[i] [j - 1].

3 - 初始化dp数组:这道题初始化这一步,有点儿不同,此前一维的时候顶多是前两个值,而二维的时候通常要考虑首行和首列的初始值。假设机器人一直向右或者向下走,路径只有一条,是不会有第二条路径的,所以要把首行和首列的值都初始化为1.

4 - 递推顺序:这道题要从上往下,从左往右遍历,为什么?因为“机器人只能向右或者向下走”,说明机器人只能来自于当前位置的上方,或是下方。

5 - 举例:略

根据以上思想,本题代码如下:

int uniquePaths(int m, int n) {
    
    
    vector<vector<int>> dp(m, vector<int>(n, 0));
    for (int i = 0; i < m; ++i) dp[i][0] = 1;
    for (int i = 0; i < n; ++i) dp[0][i] = 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];
}

之所以这道题也可以用一维数组求解,也很好理解,上述代码中,在二重循环内部,更新dp数组的时候 i 的值并没有变,即行数没变,所以我们只用一个数组就可以表示,如果要说有什么意义的话,那就是移动到第j列的时候,一共有多少种路径。根据这个思路,代码如下:

int uniquePaths(int m, int n) {
    
    
    vector<int> dp(n);
    for (int i = 0; i < n; ++i) dp[i] = 1;
    for (int j = 1; j < m; ++j) {
    
    
        for (int i = 1; i < n; ++i) {
    
    
            dp[i] += dp[i - 1];
        }
    } 
    return dp[n - 1];
}

最后,上面的思路都是先考虑行数,其实先考虑列也是一样的,相当于把m和n的值交换了一下嘛


63 不同路径II medium

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

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

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

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

还是从五部曲入手:

1 - 确定dp[i] [j]数组:和上一题一样

2 - 递推公式:这道题中有障碍物,那么就要根据障碍物来计算递推公式。分为两种情况,第i 行第j 列存在障碍物和不存在障碍物。如果存在,就应该跳过,因为没有道路可以通向该点;但如果不存在,那么还是和上一题一样。

3 - 初始化dp数组:不同之处在于,遇到障碍就应该停止初始化。

4 - 遍历顺序:从上到下,从左到右

5 - 举例:省略

根据以上思路,代码如下:

int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
    
    
    int m = obstacleGrid.size();
    int n = obstacleGrid[0].size();
    if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) return 0;
    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 i = 0; i < n && obstacleGrid[0][i] == 0; ++i)
        dp[0][i] = 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];
}

与上一题相同的是,这道题在遍历的时候,i的值同样没有变,所以还是可以考虑用一维数组滚动表示二维数组,此处直接借鉴随想录中给出的代码:

int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
    
    
    if (obstacleGrid[0][0] == 1)
        return 0;
    vector<int> dp(obstacleGrid[0].size());
    for (int j = 0; j < dp.size(); ++j)
        if (obstacleGrid[0][j] == 1)
            dp[j] = 0;
    else if (j == 0)
        dp[j] = 1;
    else
        dp[j] = dp[j-1];

    for (int i = 1; i < obstacleGrid.size(); ++i)
        for (int j = 0; j < dp.size(); ++j){
    
    
            if (obstacleGrid[i][j] == 1)
                dp[j] = 0;
            else if (j != 0)
                dp[j] = dp[j] + dp[j-1];
        }
    return dp.back();
}

猜你喜欢

转载自blog.csdn.net/qq_41205665/article/details/128925856
今日推荐