63. Unique Paths II(不同路径 II)两种解法(C++ & 注释)

1. 题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

在这里插入图片描述
网格中的障碍物和空位置分别用 1 和 0 来表示。

说明:m 和 n 的值均不超过 100。

示例 1:

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

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

题目链接:中文题目英文题目

2. 回溯法(Backtracking, Time Limit Exceeded)

2.1 解题思路

这题和62. Unique Paths(不同路径)的回溯法思路基本相似,只是这题会遇到障碍物,所以当遇到障碍物,我们则跳过当前坐标,返回上一步继续向右或向下寻找抵达终点的路径,不过这方法超时预订。

具体的讲解可以参考:62. Unique Paths(不同路径)

2.2 实例代码

class Solution {
    int count = 0;

    void move(int i, int j, int iFinish, int jFinish, vector<vector<int>>& obstacleGrid) {
        if (i >= iFinish + 1 || j >= jFinish + 1 || obstacleGrid[i][j]) return;
        if (i == iFinish && j == jFinish) { count++; return; }
        move(i, j + 1, iFinish, jFinish, obstacleGrid);
        move(i + 1, j, iFinish, jFinish, obstacleGrid);
    }
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (!obstacleGrid.size()) return this->count;
        move(0, 0, obstacleGrid.size() - 1, obstacleGrid[0].size() - 1, obstacleGrid);
        return this->count;
    }
};

3. 动态规划(Dynamic Programming)

3.1 解题思路

同样,对回溯法进行空间的优化思路也是和上题是一致的,遇到障碍物返回0即可。这里可以创建一个额外的二维数组存储结果,也可以直接修改题目给的obstacleGrid二维数组。

如果使用第二种方法,需要把obstacleGrid预处理一下,把里面表示障碍物的1,全部变成-1,如果还是保留之前的1,后面遇到重复情况时,会把障碍物误认为有一个路径数的坐标。

最后就是用迭代实现了,迭代的思路还是和上一题基本类似,只有两点不同:

  1. 处理右边界和下边界,或上边界和左边界时,需要先预处理终点([m - 1][n - 1])或起点([0][0])是否为1,,然后操作边界时,当前坐标的值需要保证不为1(障碍物),而且前一个坐标值不能0,只有满足这两个条件,这个坐标值才能等于1,也就是保证了这条路径是通向终点的;
  2. 处理中间部分的坐标时,如果原值为1(障碍物)时,直接赋值0;反之,按照前一题的公式进行计算即可:dp[i][j] = dp[i + 1][j] + dp[i][j + 1] 或 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

具体的讲解可以参考:62. Unique Paths(不同路径)

番外:3.2.3 迭代(Iteration) - 从上到下,这个方法的代码不知道为什么线上有一个测试案例无法通过,显示int类型越界,但是笔者本地测试可以得到正确答案,也不知道为什么…测试案例详情,我已经写在这部分代码里面了,如果有童鞋发现了原因,麻烦告知一下啦,Thanks♪(・ω・)ノ

3.2 实例代码

3.2.1 递归(Recursion) - 需要额外空间

class Solution {
    int move(int i, int j, int iFinish, int jFinish, vector<vector<int>>& obstacleGrid, vector<vector<int>>& memory) {
        if (i >= iFinish + 1 || j >= jFinish + 1 || obstacleGrid[i][j]) return 0;
        if (memory[i][j]) return memory[i][j];
        if (i == iFinish && j == jFinish) return 1;
        memory[i][j] = move(i, j + 1, iFinish, jFinish, obstacleGrid, memory) + move(i + 1, j, iFinish, jFinish, obstacleGrid, memory);
        return memory[i][j];
    }
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (!obstacleGrid.size()) return 0;
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        vector<vector<int>> memory(m, vector<int>(n, 0));
        memory[0][0] = move(0, 0, m - 1, n - 1, obstacleGrid, memory);
        return memory[0][0];
    }
};

3.2.2 递归(Recursion) - 不需要额外空间

class Solution {
    int move(int i, int j, int iFinish, int jFinish, vector<vector<int>>& obstacleGrid) {
        if (i >= iFinish + 1 || j >= jFinish + 1 || obstacleGrid[i][j] == -1) return 0;
        if (obstacleGrid[i][j]) return obstacleGrid[i][j];
        if (i == iFinish && j == jFinish) return 1;
        obstacleGrid[i][j] = move(i, j + 1, iFinish, jFinish, obstacleGrid) + move(i + 1, j, iFinish, jFinish, obstacleGrid);
        return obstacleGrid[i][j];
    }
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (!obstacleGrid.size()) return 0;
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();

		// 预处理obstacleGrid
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (obstacleGrid[i][j]) obstacleGrid[i][j] = -1;

        obstacleGrid[0][0] = move(0, 0, m - 1, n - 1, obstacleGrid);
        return obstacleGrid[0][0];
    }
};

3.2.3 迭代(Iteration) - dp[i][j] = dp[i + 1][j] + dp[i][j + 1]

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (!obstacleGrid.size()) return 0;
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        if (obstacleGrid[m - 1][n - 1]) return 0;
        obstacleGrid[m - 1][n - 1] = 1;

        for (int i = m - 2; i >= 0; i--) 
        	obstacleGrid[i][n - 1] = (!obstacleGrid[i][n - 1] && obstacleGrid[i + 1][n - 1]) ? 1 : 0; // 右边界
        for (int i = n - 2; i >= 0; i--) 
        	obstacleGrid[m - 1][i] = (!obstacleGrid[m - 1][i] && obstacleGrid[m - 1][i + 1]) ? 1 : 0; // 下边界

        for (int i = m - 2; i >= 0; i--)
            for (int j = n - 2; j >= 0; j--)
                obstacleGrid[i][j] = obstacleGrid[i][j] ? 0 : obstacleGrid[i + 1][j] + obstacleGrid[i][j + 1];

        return obstacleGrid[0][0];
    }
};

int main()
{
    Solution s;
    // 线上无法通过测试的案例,
    vector<vector<int>> nums = {{0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0},{0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},{1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,1,0,0,0},{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,1},{0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0},{0,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0},{0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0},{1,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,1},{0,0,0,0,1,0,0,1,0,1,1,1,0,0,0,1,0,0,1,0,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0},{0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0},{1,0,1,0,1,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,0,0,1,0,0,0,1,0},{0,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1},{0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0},{1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},{0,1,0,0,1,0,0,0,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0},{0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},{0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0},{0,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},{0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,0,0,1,0,1,0,1,1,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},{0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},{1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},{0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0},{0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,1,1,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0} };
    cout << s.uniquePathsWithObstacles(nums);
 }

3.2.4 迭代(Iteration) - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (!obstacleGrid.size()) return 0;
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        if (obstacleGrid[0][0]) return 0;
        obstacleGrid[0][0] = 1;

        for (int i = 1; i < m; i++) obstacleGrid[i][0] = (!obstacleGrid[i][0] && obstacleGrid[i - 1][0]) ? 1 : 0; // 右边界
        for (int i = 1; i < n; i++) obstacleGrid[0][i] = (!obstacleGrid[0][i] && obstacleGrid[0][i - 1]) ? 1 : 0; // 上边界

        for (int i = 1; i < m; i++)
            for (int j = 1; j < n; j++)
                obstacleGrid[i][j] = obstacleGrid[i][j] ? 0 : obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1];

        return obstacleGrid[m - 1][n - 1];
    }
};

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值