1036. 逃离大迷宫
题目描述
在一个 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
。
示例 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 < 106
source != target
题目数据保证 source
和 target
不在封锁列表内
解题思路
这是第一次尝试写困难题的题解,也是一个新的挑战。
首先来分析题述,可以想到从源方格 source
遍历至目标方格 target
,若其中存在一条路径不包含 blocked
即可。
但是由于整个网格的长度和宽度是 10^6
,直接遍历的话就会产生超时问题,所以需要对问题进一步分析。
由于 blocked
的数量是有限的,考虑极限情况下,若想要尽可能多地增加被封锁方格的数量,只能是 blocked
中的方块斜向排列,与整个网格中的任意相邻的两条边组合,这样形成的被封锁区域的包围圈就是一个等腰直角三角形。
在该极限情况下,形成的的包围圈中网格数量可以用等差数列进行计算而得:
m
=
n
(
n
−
1
)
2
m = \frac{n(n-1)}{2}
m=2n(n−1)
m
为包围圈中的网格数量
n
为 blocked
的数量
接下来就可以进行搜索操作了:
- 从源头
source
出发进行搜索,经过网格的数量到m
时则可以停止; - 若过程中经过了
target
,则表示两点是可达的; - 若经过网格数量还未达到
m
便提前结束,则表示source
处于包围圈中,无法到达target
; - 然后再从
target
出发进行搜索,经过网格的数量到m
时则可以停止; - 若经过网格数量还未达到
m
便提前结束,则表示target
处于包围圈中,无法到达source
;否则说明两点都不在包围圈中,是可达的。
代码
JavaScript
/**
* @param {number[][]} blocked
* @param {number[]} source
* @param {number[]} target
* @return {boolean}
*/
var isEscapePossible = function(blocked, source, target) {
const max = 1000000;
const dir = [[0, 1], [0, -1], [1, 0], [-1, 0]];
// 障碍物的数量小于2时,无法封锁
if (blocked.length < 2) {
return true;
}
let blockedMap = new Map();
for (let b of blocked) blockedMap.set(b.join(), 1);
// 最大覆盖范围
let countdown = Math.floor(blocked.length * (blocked.length - 1) / 2);
const isBlocked = (source, target) => {
// 用set记录访问过的方格,确保不重复
let visited = new Set();
visited.add(source.join());
let q = [source];
let count = 1;
while (q.length && count <= countdown) {
let tmp = [];
for (let [x, y] of q) {
for (let d of dir) {
let nx = x + d[0];
let ny = y + d[1];
let str = nx + ',' + ny;
if (nx >= 0 && nx < max && ny >= 0 && ny < max
&& !blockedMap.get(str) && !visited.has(str)) {
visited.add(str);
tmp.push([nx, ny]);
count++;
if (nx == target[0] && ny == target[1]) {
return false;
}
}
}
}
q = tmp;
}
return count <= countdown;
}
return !isBlocked(source, target) && !isBlocked(target, source);
};