62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径
示例:
不同路径 I 、II - 图1 :::info 输入:m = 3, n = 7
输出:28 :::

思路

按照动态规划五部曲来分析:

  1. 首先确定 dp 数组及其下标的含义

dp[i][j]: 指的是从(0, 0) 到坐标(i, j)的路径数量。

  1. 动态规划递推公式

可以确定的是,在大于第一列和第一行的坐标,只能从左边元素和上方元素到达,即左边元素和上方元素的路径数量之和,即dp[i][j] = dp[i-1][j] + dp[i][j-1]

  1. 初始化

将第一行和第一列的所有元素路径数量均设为 1。

  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]一定是有数值的

  1. 举例推导数组

image.png

  1. var uniquePaths = function(m, n) {
  2. // 生成二维数组时,元素直接赋值为1
  3. const dp = new Array(m).fill().map(() => new Array(n).fill(1));
  4. for (let i = 1; i < m; i++) {
  5. for (let j = 1; j < n; j++) {
  6. dp[i][j] = dp[i-1][j] + dp[i][j-1];
  7. }
  8. }
  9. return dp[m-1][n-1];
  10. };

但是,我们其实也可以用一维数组,直接滚动遍历即可,可以优化空间。

  1. var uniquePaths = function(m, n) {
  2. const dp = new Array(n).fill(1);
  3. for (let i = 1; i < m; i++) {
  4. for (let j = 1; j < n; j++) {
  5. dp[j] += dp[j-1];
  6. }
  7. }
  8. return dp[n-1];
  9. };

63. 不同路径 II

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

思路

本道题与 62.不同路径 的思路是一致的,在上一题中,我们遇到了没有障碍物的情况,而在这道题职工,当我们遇到障碍物时,我们可以设置该元素坐标对应的dp为 0 即可。
但是,值得注意的一点是,在初始化过程中,如果第一行或第一列的某个元素为障碍物,则其后面的元素均不能到达,即为 0 。

  1. var uniquePathsWithObstacles = function(obstacleGrid) {
  2. const m = obstacleGrid.length;
  3. const n = obstacleGrid[0].length;
  4. const dp = new Array(m).fill().map(() => new Array(n).fill(0));
  5. // 初始化
  6. for (let i = 0; i < m && obstacleGrid[i][0] === 0; i++) {
  7. dp[i][0] = 1;
  8. }
  9. for (let j = 0; j < n && obstacleGrid[0][j] === 0; j++) {
  10. dp[0][j] = 1;
  11. }
  12. // 递归公式
  13. for (let i = 1; i < m; i++) {
  14. for (let j = 1; j < n; j++) {
  15. // 如果当前元素为障碍物,直接将其设置为 0
  16. dp[i][j] = obstacleGrid[i][j] === 1 ? 0 : dp[i-1][j] + dp[i][j-1];
  17. }
  18. }
  19. return dp[m-1][n-1];
  20. };