leetcode1036. 逃离大迷宫

在一个 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<Long> blockedSet = new HashSet<>();
    for (int i = 0; i < blocked.length; i++) {
        blockedSet.add(blocked[i][0] * temp + blocked[i][1]);
    }
    //不相邻的节点,可以直接忽略
    cleanBlock(blockedSet);
    //过远的节点,可以直接忽略
    Set<Long> souBlock = getFrom(blockedSet, source);
    Set<Long> tarBlock = getFrom(blockedSet, target);
    //现在的节点
    Set<Long> set = new HashSet<>();
    //历史节点
    Set<Long> 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<Long> 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<Long> 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<Long> 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<Long> getFrom(Set<Long> blockedSet, int[] source) {
    long temp = 1000000;
    while (true) {
        int len = blockedSet.size();
        Set<Long> 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<Long> blockedSet) {
    long temp = 1000000;
    Set<Long> 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<Long> 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<Long> blockedSet, Set<Long> nset, Set<Long> 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<Long> blockedSet, Set<Long> nset, Set<Long> 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);
}
发布了127 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33321609/article/details/102901713