一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: [ [0,0,0], [0,1,0], [0,0,0] ]
输出: 2
解释: 3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
题目链接:中文题目;英文题目
这题和62. Unique Paths(不同路径)的回溯法思路基本相似,只是这题会遇到障碍物,所以当遇到障碍物,我们则跳过当前坐标,返回上一步继续向右或向下寻找抵达终点的路径,不过这方法超时预订。
具体的讲解可以参考:62. Unique Paths(不同路径)
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;
}
};
同样,对回溯法进行空间的优化思路也是和上题是一致的,遇到障碍物返回0即可。这里可以创建一个额外的二维数组存储结果,也可以直接修改题目给的obstacleGrid二维数组。
如果使用第二种方法,需要把obstacleGrid预处理一下,把里面表示障碍物的1,全部变成-1,如果还是保留之前的1,后面遇到重复情况时,会把障碍物误认为有一个路径数的坐标。
最后就是用迭代实现了,迭代的思路还是和上一题基本类似,只有两点不同:
具体的讲解可以参考:62. Unique Paths(不同路径)
番外:3.2.3 迭代(Iteration) - 从上到下,这个方法的代码不知道为什么线上有一个测试案例无法通过,显示int类型越界,但是笔者本地测试可以得到正确答案,也不知道为什么…测试案例详情,我已经写在这部分代码里面了,如果有童鞋发现了原因,麻烦告知一下啦,Thanks♪(・ω・)ノ
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];
}
};