leetcode每日一题--1036.逃离大迷宫

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
题目数据保证 sourcetarget 不在封锁列表内

解题思路

这是第一次尝试写困难题的题解,也是一个新的挑战。

首先来分析题述,可以想到从源方格 source 遍历至目标方格 target,若其中存在一条路径不包含 blocked 即可。

但是由于整个网格的长度和宽度是 10^6 ,直接遍历的话就会产生超时问题,所以需要对问题进一步分析。

由于 blocked 的数量是有限的,考虑极限情况下,若想要尽可能多地增加被封锁方格的数量,只能是 blocked 中的方块斜向排列,与整个网格中的任意相邻的两条边组合,这样形成的被封锁区域的包围圈就是一个等腰直角三角形。

在该极限情况下,形成的的包围圈中网格数量可以用等差数列进行计算而得:
m = n ( n − 1 ) 2 m = \frac{n(n-1)}{2} m=2n(n1)
m 为包围圈中的网格数量
nblocked 的数量

接下来就可以进行搜索操作了:

  1. 从源头 source 出发进行搜索,经过网格的数量到 m 时则可以停止;
  2. 若过程中经过了 target,则表示两点是可达的;
  3. 若经过网格数量还未达到 m 便提前结束,则表示 source 处于包围圈中,无法到达 target
  4. 然后再从 target 出发进行搜索,经过网格的数量到 m 时则可以停止;
  5. 若经过网格数量还未达到 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);
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真的该去学习了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值