Robot in a Grid:一个机器人在处在一个r
行c
列的迷宫的左上角。机器人在每一个位置时只能向右或者向左移动,但是迷宫中有些格子是不能到达的。设计一个算法,为机器人找到一条从左上角到右下角的路径。
机器人最终到达r
行c
列(用(r, c)
表示),那么他肯定是从(r - 1, c)
或者(r, c - 1)
移动过来的,所以可以把问题转换为求解子问题:寻找一条终点为(r - 1, c)
或者(r, c - 1)
的路径。为了到达(r - 1, c)
,上一步可以在(r - 2, c)
或者(r - 1, c - 1)
;为了到达(r, c - 1)
,上一步可以在(r - 1, c - 1)
或者(r, c - 2)
,此时已经出现了重复计算。在不优化重复计算的情况下,算法的时间复杂度为O(2 ^ (r + c))
,因为每一次有两种选择,而移动的次数为r + c
。
迷宫的大小为r * c
,如果只访问每个格子一次,那么时间复杂度就可以降为O(rc)
。在上面的例子中,对(r - 1, c - 1)
进行了两次计算,所以可以将其结果保存下来,以节省第二次的重复计算。
如果把Visited[r][c] = true
放在最后的if
或者else
中,只表明该点可达或者不可达时进行重复标记,而不是所有情况。书中只对不可达点进行了重复标记。在力扣上,只对可达点进行重复标记时间大概1000ms
,只对不可达点进行重复标记时间大概30ms
,对所有点进行重复标记时间大概15ms
。
class Solution {
public:
vector<vector<int>> pathWithObstacles(vector<vector<int>>& obstacleGrid) {
Visited.assign(obstacleGrid.size(), vector<bool>(obstacleGrid[0].size(), false));
calculatePath(obstacleGrid, obstacleGrid.size() - 1, obstacleGrid[0].size() - 1);
return Path;
}
private:
vector<vector<int>> Path;
vector<vector<bool>> Visited;
bool calculatePath(const vector<vector<int>> &obstacleGrid, const int r, const int c)
{
if(r < 0 || c < 0 || obstacleGrid[r][c] == 1) return false;
if(Visited[r][c]) return false;
else Visited[r][c] = true;
vector<int> curr = { r, c };
bool bOrigin = r == 0 && c == 0;
if(bOrigin || calculatePath(obstacleGrid, r - 1, c) || calculatePath(obstacleGrid, r, c - 1)){
Path.push_back(curr);
return true;
}
else{
return false;
}
}
};