Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LeetCode题解:63. 不同路径 II,动态规划,JavaScript,详细注释 #294

Open
chencl1986 opened this issue Feb 18, 2021 · 0 comments

Comments

@chencl1986
Copy link
Owner

chencl1986 commented Feb 18, 2021

原题链接:https://leetcode-cn.com/problems/unique-paths-ii/

解题思路:

  1. 在网格中的任意一点,都有向右和向下两种走法。同时它也是从上方和左方两个位置走过来的。
  2. 那么,任意一点的走法数量,等于从起点走到上方和左方点的数量之和。
  3. 第一行和第一列都只有一种走法,就是从起点一直走到底。
  4. 我们可以用一个二维数组,画出网格中每个点的走法数量,一直递推到终点,终点存储的就是所有的走法数量。
  5. 因此动态规划的状态转移方程为:dp[i][j]=dp[i-1][j]+dp[i][j-1]
  6. 如果遇到障碍物,则该位置的走法数量为0。
/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function (obstacleGrid) {
  // 缓存网格行列的最后一位索引
  const m = obstacleGrid.length - 1;
  const n = obstacleGrid[0].length - 1;

  // 如果起点或终点是1,必然无法走到终点,路径数量为0
  if (obstacleGrid[0][0] === 1 || obstacleGrid[m][n] === 1) {
    return 0;
  }

  // 初始化第一行的dp数组,起点的值为1
  let dp = [new Array(n + 1).fill(0)];
  dp[0][0] = 1;

  // 创建第一列的初始路径数量
  for (let i = 1; i <= m; i++) {
    // 初始化当前行的初始路径数量,默认为0
    dp.push(new Array(n + 1).fill(0));

    if (obstacleGrid[i][0] === 0) {
      // 如果当前位置可以行走,数量等于上一行
      dp[i][0] = dp[i - 1][0];
    } else {
      // 如果当前位置不可行走,数量为0
      dp[i][0] = 0;
    }
  }

  // 初始化第一行的路径数量
  for (let i = 0; i <= n; i++) {
    if (obstacleGrid[0][i] === 0) {
      // 如果当前位置可以行走,数量为1
      dp[0][i] = 1;
    } else {
      // 如果当前位置不可行走,从这以后的数量都为0
      // 第一行初始化的值都为0,直接退出即可
      break;
    }
  }

  // 从第二行、第二列开始遍历整个网格,计算路径数量
  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (obstacleGrid[i][j] === 0) {
        // 如果当前位置可以行走,路径数量为从上、左两个方向走过来的数量之和
        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
      } else {
        // 如果当前位置不可行走,路径数量为0
        dp[i][j] = 0;
      }
    }
  }

  // 终点存储的是所有路径数量
  return dp[m][n];
};
  1. 实际上我们计算某个点的步数时,只需要左边和上边的值即可。
  2. 我们可以只用一个数组,而且我们每次都是从左向右生成步数,因此就有以下特点:
    • 因此对于第m行来说,它存储的就是m-1行的步数。
    • 对于第n列来说,它的n-1位置存储了n-1位置的步数。

代码优化如下:

/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function (obstacleGrid) {
  // 缓存网格行列的最后一位索引
  const m = obstacleGrid.length - 1;
  const n = obstacleGrid[0].length - 1;

  // 如果起点或终点是1,必然无法走到终点,路径数量为0
  if (obstacleGrid[0][0] === 1 || obstacleGrid[m][n] === 1) {
    return 0;
  }

  // 初始化第一行的dp数组,起点的值为1
  let dp = new Array(n + 1).fill(0);
  dp[0] = 1;

  // 从第二行、第二列开始遍历整个网格,计算路径数量
  for (let i = 0; i <= m; i++) {
    for (let j = 0; j <= n; j++) {
      // 如果当前位置是障碍物,路径数量为0
      if (obstacleGrid[i][j] === 1) {
        dp[j] = 0;
        continue;
      }

      // j为0无需更新,会一直保持上一行的值
      if (j > 0) {
        // 如果当前位置可以行走,路径数量为从上、左两个方向走过来的数量之和
        dp[j] = dp[j - 1] + dp[j];
      }
    }
  }

  // 终点存储的是所有路径数量
  return dp[n];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant