不同路径II——动态规划基础题

63 不同路径 II

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

不同路径II——动态规划基础题_第1张图片

 思路

本题在上一题 不同路径 上增加了一个条件,不同路径 中不存在障碍物,而 不同路径 II 则存在障碍物,有障碍物时机器人无法通过且无法到达有障碍物的格子(意味着如果起点就有障碍物,那么机器人就无法行走),行走方式依旧是 往下和往右 ,因此到达某一个格子的不同路径数和上面 dp[i - 1][j] 与 左边 dp[i][j - 1] 相关。但想必你一定想得到,有障碍物的格子既然走不了,那对应位置的格子要怎么求其 dp[i][j] ?

某个格子上如果有障碍物,那么机器人一定永远都到达不了该格子,那么到达该格子的不同路径数就为 0,这会不会影响下面 dp[i][j]的求解呢?我们纸上演示一下便知:

不同路径II——动态规划基础题_第2张图片

 可以清晰地看到,一个障碍物会影响 dp[i][j] 的来源,如果障碍物挡住了任意一个来源,那么该来源的不同路径数就为 0。

举个栗子,对于第三行第三列的格子来说,到达此格子机器人只能是从左边相邻的格子过来,上面的格子被障碍物挡住机器人无法穿过,因此 到达第三行第三列的格子的 不同路径数 取左边格子的不同路径数即可,而为了满足下面的递推公式,将有障碍物处的 dp[i][j] 设为 0 即可。

下面用动态规划五部曲细解。

确定 dp 数组的含义

本题 dp 数组含义和 不同路径 是一样的,代表机器人到达第 i + 1 行第 j + 1 列的格子处的不同路径数为 dp[i][j]。

确定递推公式

有了 不同路径 作为铺垫,本题的递推公式就很简单了,上面的思路中,为了使得递推公式排除障碍物的影响,我们将有障碍物的对应位置的 不同路径数 赋值为 0,这样就无需改动递推公式:

dp[i][j] = dp[i - 1][j] + dp[i][j - 1];

初始化 dp 数组

没有障碍物时,我们将第一行和第一列的 dp[i][j] 都初始化为 1,因为一步走到底都只有一种路径,而当有了障碍物时,题目没有限制障碍物的位置,因此障碍物可以是在任意一个格子,因为我们需要初始化的是第一行和第一列,那么初始化时只需要看第一行及第一列是否有障碍物,有障碍物怎么处理?

当遇到了障碍物,遇到障碍物之前的格子的不同路径数就是 1 (因为只能沿直线走),遇到障碍物时,障碍物以及障碍物后面的格子的不同路径数都是 0,毕竟都被挡住了,机器人不可能到达。因此 dp 初始化如下:

//初始化dp数组
for(int i = 0; i < n; i++){
    if(obstacleGrid[0][i] == 1) break;
    dp[0][i] = 1;
}
for(int i = 0; i < m; i++) {
    if(obstacleGrid[i][0] == 1) break;
    dp[i][0] = 1;
}

确定遍历顺序

依旧看递推公式 :dp[i][j] = dp[i - 1][j] + dp[i][j - 1] ,求 dp[i][j] 需要已知左边以及上面格子的 dp[i][j],故而遍历顺序是 从上到下、从左到右 。

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];
    }
}

举例推导 dp 数组

不同路径II——动态规划基础题_第3张图片

 代码实现

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if(obstacleGrid[0][0] == 1)return 0;
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        //初始化dp数组
        for(int i = 0; i < n; i++){
            if(obstacleGrid[0][i] == 1) break;
            dp[0][i] = 1;
        }
        for(int i = 0; i < m; i++) {
            if(obstacleGrid[i][0] == 1) break;
            dp[i][0] = 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];
    }
}

《代码随想录》刷题记

你可能感兴趣的:(代码随想录刷题记,动态规划,算法)