🚩传送门:牛客题目
力扣题目

题目

机器人在 [NC]325. 不同路径的数目 II - 图1 大小的地图的左上角(起点),每次可以 向下 向右 移动。要到达地图的右下角(终点)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径 ?
网格中的障碍物和空位置分别用[NC]325. 不同路径的数目 II - 图2[NC]325. 不同路径的数目 II - 图3 来表示。

示例 1:
image.png

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] 输出:2 解释:3x3 网格的正中间有一个障碍物。 从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右

解题思路:动态规划

👉 定义:[NC]325. 不同路径的数目 II - 图5 来表示从坐标 [NC]325. 不同路径的数目 II - 图6 到坐标[NC]325. 不同路径的数目 II - 图7 的路径总数 。

[NC]325. 不同路径的数目 II - 图8 表示坐标 [NC]325. 不同路径的数目 II - 图9是否可行,如果坐标[NC]325. 不同路径的数目 II - 图10有障碍物,[NC]325. 不同路径的数目 II - 图11 ,否则 [NC]325. 不同路径的数目 II - 图12

「机器人每次只能向下或者向右移动一步」,所以从坐标 [NC]325. 不同路径的数目 II - 图13 到坐标[NC]325. 不同路径的数目 II - 图14 的路径总数的值只取决于 [NC]325. 不同路径的数目 II - 图15[NC]325. 不同路径的数目 II - 图16 的和。当坐标 [NC]325. 不同路径的数目 II - 图17本身有障碍的时候,任何路径都到到不了[NC]325. 不同路径的数目 II - 图18

状态转移方程:
[NC]325. 不同路径的数目 II - 图19[NC]325. 不同路径的数目 II - 图20

边界情况:

  1. 起点终点 有障碍物,直接返回 [NC]325. 不同路径的数目 II - 图21
  2. 边界[NC]325. 不同路径的数目 II - 图22 行,[NC]325. 不同路径的数目 II - 图23列,正常全部为 [NC]325. 不同路径的数目 II - 图24,只有一种路径可达,一直向左向下
  3. [NC]325. 不同路径的数目 II - 图25出现障碍物后, [NC]325. 不同路径的数目 II - 图26列下面的全部不可达
  4. [NC]325. 不同路径的数目 II - 图27出现障碍物后, [NC]325. 不同路径的数目 II - 图28行后面的全部不可达

复杂度分析

时间复杂度:[NC]325. 不同路径的数目 II - 图29,其中 [NC]325. 不同路径的数目 II - 图30 为网格的行数,[NC]325. 不同路径的数目 II - 图31 为网格的列数 。

空间复杂度:[NC]325. 不同路径的数目 II - 图32,利用滚动数组优化,我们可以只用 [NC]325. 不同路径的数目 II - 图33 大小来记录当前的 [NC]325. 不同路径的数目 II - 图34

我的代码

  1. class Solution {
  2. public static int uniquePathsWithObstacles(int[][] obstacleGrid) {
  3. // 数组空
  4. if (obstacleGrid == null || obstacleGrid.length == 0) return 0;
  5. // 起点终点有障碍物
  6. if (obstacleGrid[0][0] == 1 ||obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] == 1) return 0;
  7. int[][] dp = new int[obstacleGrid.length][obstacleGrid[0].length];
  8. for (int i = 0; i < obstacleGrid.length; i++) {
  9. for (int j = 0; j < obstacleGrid[i].length; j++) {
  10. // 起点
  11. if (i == 0 && j == 0) {
  12. dp[0][0] = 1;
  13. continue;
  14. }
  15. // 0行
  16. if (i == 0) {
  17. // 有障碍物置零,无障碍物状态从左面转移
  18. if (obstacleGrid[0][j] == 1) dp[0][j] = 0;
  19. else
  20. dp[0][j] = dp[0][j - 1];
  21. continue;
  22. }
  23. // 0列
  24. if (j == 0) {
  25. // 有障碍物置零,无障碍物状态从上面转移
  26. if (obstacleGrid[i][0] == 1) dp[i][0] = 0;
  27. else
  28. dp[i][0] = dp[i - 1][0];
  29. continue;
  30. }
  31. // 非0行0列
  32. if (obstacleGrid[i][j] == 1) {
  33. dp[i][j] = 0;
  34. } else {
  35. // 无障碍物状态从上面、左面转移
  36. dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
  37. }
  38. }
  39. }
  40. return dp[obstacleGrid.length - 1][obstacleGrid[0].length - 1];
  41. }
  42. }

解题思路:深搜

备注:对于题目有时间限制的可能过不了,会显示超时

从起点开始按照 右下 两个方向深搜,遇到 终点 ,结果路径增[NC]325. 不同路径的数目 II - 图35,搜索整个地图的所有路径可能性,找出最终答案数 。

我的代码

  1. class Solution {
  2. int res = 0;
  3. // 可行用0表示,障碍物用1表示,临时访问置1
  4. public int uniquePathsWithObstacles(int[][] obstacleGrid) {
  5. // 非法判断
  6. if (obstacleGrid == null || obstacleGrid.length == 0 || obstacleGrid[0][0] == 1 ||
  7. obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] == 1)
  8. return 0;
  9. dfs(0, 0, obstacleGrid);
  10. return res;
  11. }
  12. private void dfs(int i, int j, int[][] obstacleGrid) {
  13. // 判断边界合法性
  14. if (i < 0 || i >= obstacleGrid.length || j < 0 || j >= obstacleGrid[i].length) return;
  15. // 判断当前位置是否可行
  16. if (obstacleGrid[i][j] == 1) return;
  17. // 判断是否到达终点
  18. if (i == obstacleGrid.length - 1 && j == obstacleGrid[0].length - 1) {
  19. res++;
  20. return;
  21. }
  22. // 标记当前位置被访问
  23. obstacleGrid[i][j]=1;
  24. // 右下
  25. int[][] dir = new int[][]{{0, 1}, {1, 0}};
  26. for (int k = 0; k < dir.length; k++) {
  27. dfs(i + dir[k][0], j + dir[k][1], obstacleGrid);
  28. }
  29. // 释放当前位置的访问
  30. obstacleGrid[i][j]=0;
  31. }
  32. }