在一个 10^6 x 10^6 的网格中,每个网格块的坐标为 (x, y),其中 0 <= x, y < 10^6。
我们从源方格 source 开始出发,意图赶往目标方格 target。每次移动,我们都可以走到网格中在四个方向上相邻的方格,只要该方格不在给出的封锁列表 blocked 上。
只有在可以通过一系列的移动到达目标方格时才返回 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 <= blocked[i][j] < 10^6
source.length == target.length == 2
0 <= source[i][j], target[i][j] < 10^6
source != target
这道题几个月前就看到,一直没有写,没有思路,按原来的方式从起点往终点走,肯定会超时,因为网格过大。
本道题的提示在于:0 <= blocked.length <= 200
我先开始的思路,障碍格子最多200个,那我从起点走400步,如果能遇到目的地,那就成功,如果走了400步还没有结束,那至少走出包围圈,如果未走出包围圈,那就是失败。走出包围圈后,目的地开始出发,走400步,如果也能走出来,说明也走出包围圈,那海阔凭鱼跃,天高任鸟飞,肯定能相遇。
但是,代码执行下来,五百多毫秒,次次都执行400次,确实太耗时。
所以进行改良处理,改良内容如下:
1、落单障碍格子不考虑(障碍格附近没有其他格子),因为落单障碍格不会阻挡前进的脚步
2、离得过远的障碍格子不考虑(离目标节点的距离大于格子的数目),里目标节点远,起不到阻碍的效果,可以绕道
3、循环次数根据起点的位置不同而不同(如果位置偏中间,阻碍格子得绕一个圈,循环次数可以偏少,如果偏角落,阻碍格子可以借助边界,循环次数要多点)
改良结束之后,代码执行时间为46ms
代码如下:
public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
long temp = 1000000;
Set blockedSet = new HashSet<>();
for (int i = 0; i < blocked.length; i++) {
blockedSet.add(blocked[i][0] * temp + blocked[i][1]);
}
//不相邻的节点,可以直接忽略
cleanBlock(blockedSet);
//过远的节点,可以直接忽略
Set souBlock = getFrom(blockedSet, source);
Set tarBlock = getFrom(blockedSet, target);
//现在的节点
Set set = new HashSet<>();
//历史节点
Set setH = new HashSet<>();
long v = source[0] * temp + source[1];
set.add(v);
setH.add(v);
long tar = target[0] * temp + target[1];
for (int i = 0; i < getLen(source, souBlock); i++) {
if (set.size() > 0) {
Set nset = new HashSet<>();
for (Long l : set) {
addNset(l, blockedSet, nset, setH, temp);
}
set = nset;
//如果走到目标节点,那就是成功
if (set.contains(tar)) {
return true;
}
} else {
//无路可走,说明封闭空间,那就是失败
return false;
}
}
set.clear();
setH.clear();
v = target[0] * temp + target[1];
set.add(v);
setH.add(v);
for (int i = 0; i < getLen(target, tarBlock); i++) {
if (set.size() > 0) {
Set nset = new HashSet<>();
for (Long l : set) {
addNset(l, blockedSet, nset, setH, temp);
}
set = nset;
} else {
//无路可走,说明封闭空间,那就是失败
return false;
}
}
return true;
}
private int getLen(int[] source, Set souBlock) {
long temp = 1000000;
if (source[0] <= 100 || temp - source[0] <= 100) {
return souBlock.size() * 2 + 1;
}
if (source[1] <= 100 || temp - source[1] <= 100) {
return souBlock.size() * 2 + 1;
}
return souBlock.size() + 1;
}
/**
* 此方法就一个目的,就是只保留留得近的节点,因为远处节点无效
*/
private Set getFrom(Set blockedSet, int[] source) {
long temp = 1000000;
while (true) {
int len = blockedSet.size();
Set nset = new HashSet<>();
for (Long l : blockedSet) {
long x = l / temp;
long y = l % temp;
if (Math.abs(x - source[0]) <= len && Math.abs(y - source[1]) <= len) {
nset.add(l);
}
}
if (nset.size() == len) {
return nset;
}
blockedSet = nset;
}
}
/**
* 把那些孤独的节点删除(孤独节点,周围8个位置,没有blocked)
*/
private void cleanBlock(Set blockedSet) {
long temp = 1000000;
Set removeSet = new HashSet<>();
for (Long l : blockedSet) {
long x = l / temp;
long y = l % temp;
if (contans(x - 1, y - 1, blockedSet)) {
continue;
}
if (contans(x - 1, y, blockedSet)) {
continue;
}
if (contans(x - 1, y + 1, blockedSet)) {
continue;
}
if (contans(x, y - 1, blockedSet)) {
continue;
}
if (contans(x, y + 1, blockedSet)) {
continue;
}
if (contans(x + 1, y - 1, blockedSet)) {
continue;
}
if (contans(x + 1, y, blockedSet)) {
continue;
}
if (contans(x + 1, y + 1, blockedSet)) {
continue;
}
}
blockedSet.removeAll(removeSet);
}
private boolean contans(long x, long y, Set blockedSet) {
long temp = 1000000;
if (x < 0) {
return false;
}
if (y < 0) {
return false;
}
if (x >= temp) {
return false;
}
if (y >= temp) {
return false;
}
return blockedSet.contains(x * temp + y);
}
private void addNset(Long l, Set blockedSet, Set nset, Set setH, long temp) {
long x = l / temp;
long y = l % temp;
addNset(x - 1, y, blockedSet, nset, setH, temp);
addNset(x + 1, y, blockedSet, nset, setH, temp);
addNset(x, y - 1, blockedSet, nset, setH, temp);
addNset(x, y + 1, blockedSet, nset, setH, temp);
}
private void addNset(long x, long y, Set blockedSet, Set nset, Set setH, long temp) {
if (x < 0) {
return;
}
if (y < 0) {
return;
}
if (x >= temp) {
return;
}
if (y >= temp) {
return;
}
long v = x * temp + y;
if (blockedSet.contains(v)) {
return;
}
if (setH.contains(v)) {
return;
}
nset.add(v);
setH.add(v);
}