题目信息
在一个 10^6 x 10^6 的网格中,每个网格上方格的坐标为 (x, y) 。
现在从源方格 source = [sx, sy] 开始出发,意图赶往目标方格 target = [tx, ty] 。数组 blocked 是封锁的方格列表,其中每个 blocked[i] = [xi, yi] 表示坐标为 (xi, yi) 的方格是禁止通行的。
每次移动,都可以走到网格中在四个方向上相邻的方格,只要该方格 不 在给出的封锁列表 blocked 上。同时,不允许走出网格。
只有在可以通过一系列的移动从源方格 source 到达目标方格 target 时才返回 true。否则,返回 false。
0 <= blocked.length <= 200
题目解析
很容易让人想到用dfs或bfs遍历所有格子,来确定能否到达终点,但此题的数据范围太大,10^6大概率会超时,所以需要另辟蹊径。
题目告诉我们blocked.length在[0,200] , 所以说大部分的位置都是空的,可以走的,那什么情况下无法赶往目标方格呢,就是入口或出口被分别包围起来了。
那我们只要证明,入口和出口没有被包围,就可以说明一定可以从源点走到目标点的。
那么怎么确认有没有被包围,就是我们要解决的问题了:
由题目可知,blocked的大小最多只有200个,而大迷宫是个10^6 * 10^6的超大迷宫,所以最大面积只能是它作为一条斜边依赖于两个墙角。
如果你不能理解,代码里的MAX也是可以设置为200 * 200(甚至是1e5),假设了超过blocked的个数一倍的大小。
之后,利用bfs的方式,分别判断入口和出口有没有被包围即可!
代码
cpp
class Solution {
public:
int EDGE = 1e6 , MAX = 1e5;
int BASE = 131;
unordered_set<long long> set;
int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
bool isEscapePossible(vector<vector<int>>& blocked, vector<int>& s, vector<int>& t) {
int n = blocked.size();
for(auto& p : blocked)
set.insert(p[0]*BASE+p[1]);
MAX = n * (n-1) / 2;
return check(s,t) && check(t,s);
}
bool check(vector<int>& a, vector<int>& b){
unordered_set<long long> vis;
queue<pair<int,int> > q;
q.push({a[0],a[1]});
while(q.size() > 0 && vis.size() <= MAX){
auto t = q.front();
q.pop();
int x = t.first, y = t.second;
if(x == b[0] && y == b[1]) return true;
vis.insert(x * BASE + y);
for(int i = 0;i<4;i++){
int nx = x + dir[i][0], ny = y + dir[i][1];
if(nx < 0 || nx >= EDGE || ny < 0 || ny >= EDGE){
continue;
}
if(set.count(nx * BASE + ny)) continue;
if(vis.count(nx * BASE + ny)) continue;
q.push({nx , ny});
vis.insert(nx * BASE + ny);
}
}
return vis.size() > MAX;
}
};