力扣1036——逃离大迷宫(有限步BFS)

题目(困难)

在一个 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,则说明已经封锁不住了。
而最大封锁范围的确定,用一点贪心的想法,给你有限个点,在网格中围最大的面积;
想到了围棋上的金角银边草肚皮,在角上的最好围空的,最大程度借助边界帮忙封锁;
所以最大范围就是在角上的一个等腰三角形,搬运官方题解图
image.png
该范围的面积为为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;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值