题目(困难)
在一个 1 0 6 × 1 0 6 10^6 × 10^6 106×106的网格中,每个网格上方格的坐标为 (x, y) 。
现在从源方格 source = [sx, sy] 开始出发,意图赶往目标方格 target = [tx, ty] 。数组 blocked 是封锁的方格列表,其中每个 blocked[i] = [xi, yi] 表示坐标为 (xi, yi) 的方格是禁止通行的。
每次移动,都可以走到网格中在四个方向上相邻的方格,只要该方格 不 在给出的封锁列表 blocked 上。同时,不允许走出网格。
只有在可以通过一系列的移动从源方格 source 到达目标方格 target 时才返回 true。否则,返回 false。
示例 1:
输入:blocked = [[0,1],[1,0]], source = [0,0], target = [0,2]
输出:false
解释:
从源方格无法到达目标方格,因为我们无法在网格中移动。
无法向北或者向东移动是因为方格禁止通行。
无法向南或者向西移动是因为不能走出网格。
示例 2:
输入:blocked = [], source = [0,0], target = [999999,999999]
输出:true
解释:
因为没有方格被封锁,所以一定可以到达目标方格。
提示:
0 <= blocked.length <= 200
blocked[i].length == 2
0 <= xi, yi < 106
source.length == target.length == 2
0 <= sx, sy, tx, ty < 10^6
source != target
题目数据保证 source 和 target 不在封锁列表内
解题思路
看了题解明白要从数据范围反向入手,0 <= blocked.length <= 200
如果这么少的封锁块已经不能将起止点之一给封锁住,则一定能连通;
假设能封锁的最大范围是MAX,则如果通过BFS能走到的位置已经超过MAX,则说明已经封锁不住了。
而最大封锁范围的确定,用一点贪心的想法,给你有限个点,在网格中围最大的面积;
想到了围棋上的金角银边草肚皮,在角上的最好围空的,最大程度借助边界帮忙封锁;
所以最大范围就是在角上的一个等腰三角形,搬运官方题解图
该范围的面积为为n * (n - 1) / 2;
所以解题过程为,从起止点分别进行bfs,如果能探索的点超过MAX限制,则可以连通;
过程中用哈希set存储封锁点以及已访问的点;
另外借助哈希的思想,用一个数表示某个点的坐标,即 横坐标*base + 纵坐标, base取质数13131
代码
class Solution {
public:
long long base = 13131;
long long MAX;
unordered_set<long long> block_set;
vector<vector<int>> dric = {{0,1}, {0,-1}, {1,0}, {-1,0}};
bool isEscapePossible(vector<vector<int>>& blocked, vector<int>& source, vector<int>& target) {
long long n = blocked.size();
MAX = n * (n - 1) / 2;
for(auto& b : blocked) {
block_set.insert(b[0] * base + b[1]);
}
return check(source, target) && check(target, source);
}
bool check(vector<int>& a, vector<int>& b) { //从a找b
unordered_set<long long> visd; //记录访问过的位置
queue<pair<int, int> > q; //bfs
q.push({a[0], a[1]});
visd.insert(a[0] * base + a[1]);
while(!q.empty()) {
pair<int, int> tmp = q.front();
q.pop();
for(int i = 0; i < 4; i++) {
pair<int, int> next = {tmp.first + dric[i][0], tmp.second + dric[i][1]};
if(next.first == b[0] && next.second == b[1]) return true; //直接找到
if(next.first < 0 || next.first >= 1e6 || next.second < 0 || next.second >= 1e6) //越界
continue;
long long nextval = next.first * base + next.second;
if(block_set.count(nextval)) continue; //该位置被封锁;
if(visd.count(nextval)) continue; //该位置已被访问过;
q.push(next);
visd.insert(nextval);
}
if(visd.size() > MAX) return true;
}
return false;
}
};