「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」
题目
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
示例 1:
-
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
-
输出:2 解释:
-
3x3 网格的正中间有一个障碍物。
-
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
示例 2:
- 输入: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] = dp[i - 1][j] + dp[i][j - 1],最后,我们需要注意的是处在(i,j)位置如果是障碍物,那么dp[i][j] = 0。我们只需要初始化第一列和第一行的dp数组,然后按照遍历顺序依次递推dp数组。
- 确定dp数组以及下标的含义
dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。
- 确定递推公式
想要求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]只有这两个方向过来。
- dp数组的初始化
如何初始化呢,原本dp[i][0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0][j]也同理,但是本题有障碍物,我们只需要读取对应obstacleGrid,如果为1,则是有障碍物,将对应dp数组值置为0
for (let i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
dp[i][0] = 1;
}
for (let i = 0; i < n && obstacleGrid[0][i] == 0; i++) {
dp[0][i] = 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]一定是有数值的。此外我们还需要注意障碍问题,这里做一个判断,当 obstacleGrid[i][j]=== 1时,dp[i][j] = 0;
- 举例推导dp数组
var uniquePathsWithObstacles = function(obstacleGrid) {
let m = obstacleGrid.length;
let n = obstacleGrid[0].length;
let dp = new Array(m).fill().map(item => Array(n).fill(0));
for (let i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
dp[i][0] = 1;
}
for (let i = 0; i < n && obstacleGrid[0][i] == 0; i++) {
dp[0][i] = 1;
}
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
dp[i][j] = obstacleGrid[i][j] === 1 ? 0 : dp[i - 1][j] + dp[i][j - 1]
}
}
return dp[m-1][n-1];
};