逃离大迷宫-力扣

题目

在一个 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 不在封锁列表内

题解

        本题中给定的网格规模是 10^6 * 10^6 的,常规的广度优先搜索会达到 O(10^6×10^6)=O(10^12) 的时间复杂度,远远超出了时间限制。因此我们必须进行优化。

使用有限步数的广度优先搜索

分析从 source 无法走到 target 的情况,无非就是以下两种:

  1. source 被障碍完全包围,并且 target 不在包围圈中;
  2. target 被障碍完全包围,并且 source 不在包围圈中。

在「障碍的个数不超过 200 个前提下」,我们可以确定「包围圈」的大小不会很大。

当障碍物为n个时,由图可知包围圈最大为(1+2+...+n-1)=  n*(n-1)/2 个。所以题目中包围圈最大也不过19900个。

算法过程

  1. 我们从 source 开始进行广度优先搜索。如果经过了不超过 n*(n-1)/2 个非障碍位置就已经结束搜索,说明  source 在「包围圈」中。但如果我们在过程中经过了 target,那么说明它们是可达的,否则一定不可达可以直接返回结果。
  2. 我们再从 target 开始进行广度优先搜索。如果经过了不超过 n*(n-1)/2 个非障碍位置就已经结束搜索,说明 target 在「包围圈」中,直接返回失败。
  3. 若source 和 target 均不在「包围圈」中,此时一定可达。
class Solution {
private:
    //n*(n-1)/2 19900
    struct node{
        int x,y;
        bool operator == (const node &t) const {
            return  x==t.x && y==t.y;
        }
    };
    struct NodeHash {
        std::size_t operator () (const node &t) const {
            return  t.x * 100 + t.y;
        }
    };
    std::unordered_map<node,bool,NodeHash> bk;//标记坐标是否可走
    int maxx;    
public:

    bool check(int a){
        return (a>=0 && a<=999999);
    }

    int bfs(vector<int> start,vector<int> end){
        queue<node> que;
        int ne[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};
        int num = 0;
        que.push({start[0],start[1]});
        bk[{start[0],start[1]}] = true;
        num++;
        while(que.size()){
            int x = que.front().x;
            int y = que.front().y;
            que.pop();
            for(int i = 0; i < 4; i++ ){
                int tx = x + ne[i][0];
                int ty = y + ne[i][1];
                if(check(tx) && check(ty) && !bk[{tx,ty}]){
                    que.push({tx,ty});
                    num++;
                    if(tx == end[0] && ty == end[1]) return true;
                    if(num > maxx) return true; //没有被包围
                    bk[{tx,ty}] = true;
                }
            }
        }
        return false; //被障碍或边界包围 且 起点与终点不在同个包围圈中
    }

    bool isEscapePossible(vector<vector<int>>& blocked, vector<int>& source, vector<int>& target) {
        maxx = blocked.size()*(blocked.size()-1)/2;

        for(auto tmp: blocked){
            bk[{tmp[0],tmp[1]}] = true;
        }
        int f1 = bfs(source,target);
        if(!f1) return false; //被障碍或边界包围 且 起点与终点不在同个包围圈中
		
        bk.clear();
        for(auto tmp: blocked){
            bk[{tmp[0],tmp[1]}] = true;
        }
        int f2 = bfs(target,source);
        if(!f2) return false; //被障碍或边界包围 且 起点与终点不在同个包围圈中
        
        return true; //两者均没被包围 或 在同个包围圈中
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值