动态规划基础部分06不同路径 II

98 阅读4分钟

不同路径 II

力扣63. 不同路径 II - 力扣(LeetCode)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
示例 1:
image.png
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右
    示例 2:
    image.png

输入:obstacleGrid = [[0,1],[0,0]]
输出:1
提示:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j] 为 0 或 1
这道题相对于上一篇文章中不同路径 就是有了障碍。第⼀次接触这种题⽬的同学可能会有点懵,这有障碍了,应该怎么算呢?不同路径中我们已经详细分析了没有障碍的情况,有障碍的话,其实就是标记对应的dp数组[i][j]保持初始值(0)就可以了。

动规五部曲:

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

和上一篇文章一样因为机器⼈从(0 , 0) 位置触发,到(m - 1, n - 1)终点。所以定义二维数组dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。

2. 确定递推公式

递推公式也和上一篇文章不同路径⼀样,dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。但这⾥需要注意⼀点,因为有了障碍,dp[i][j] == 1就是障碍的应该就保持初始状态(初始状态为0)。
所以Java代码为:

    if(obstacleGrid[i][j] == 0){
        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }

3. dp数组如何初始化

在上一篇文章不同路径中我们给出如下的初始化:

    int[][] dp = new int[m][n];   //初始化都为0
    for (int i = 0; i < m; i++) dp[i][0] = 1; 
    for (int j = 0; j < n; j++) dp[0][j] = 1;

因为从(0, 0)的位置到(i, 0)的路径只有⼀条,所以dp[i][0]⼀定为1,dp[0][j]也同理。但如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是⾛不到的位置了,所以障碍之后的dp[i][0]应该还是初始值0。
如图:

image.png
所以本题代码初始化为:

        for(int i = 0; i < x && obstacleGrid[i][0] == 0; i++){
            dp[i][0] = 1;
        }   
        for(int i = 0; i < y && obstacleGrid[0][i] == 0; i++){;
            dp[0][i] = 1;
        }

注意代码⾥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]⼀定是有数值。

5. 举例推导dp数组

用图一推导:
image.png

image.png
如果这个图看不懂,建议在理解⼀下递归公式,然后照着⽂章中说的遍历顺序,⾃⼰推导⼀下! 动规五部分分析完毕,对应Java代码如下:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int x = obstacleGrid.length;
        int y = obstacleGrid[0].length;
        int[][] dp = new int[x][y];
        for(int i = 0; i < x && obstacleGrid[i][0] == 0; i++)
            dp[i][0] = 1; 
        for(int i = 0; i < y && obstacleGrid[0][i] == 0; i++)
            dp[0][i] = 1;
        for(int i = 1; i < x; i++){
            for(int j = 1; j < y; j++){
                if(obstacleGrid[i][j] == 0)
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                else
                    continue;
            }
        }
        return dp[x - 1][y - 1];
    }
}

本题是62.不同路径的障碍版,整体思路⼤体⼀致。但就算是做过62.不同路径,在做本题也会有感觉遇到障碍⽆从下⼿。其实只要考虑到,遇到障碍dp[i][j]保持0就可以了。也有⼀些⼩细节,例如:初始化的部分,很容易忽略了障碍之后应该都是0的情况。