2022.01.11 Leetcode 每日一题 逃离大迷宫

1、题目

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

2、分析

首先,xy的范围是1e6,二维平面就是1e12个点,暴力bfs的话,一定超时。所以要怎么做呢?

1. 方法1

第一个方法,也是我比较喜欢的方法就是离散化。可以参考离散化。在Acwing基础课里讲了一维的离散化,正好这题看看二维的离散化。
观察到block只有200个,而整个图有这么大,离散化正合适。
首先明确,二维离散化需要对行和列分别处理,因为行和行列和列之间需要保持一定的关系。
所谓离散化就是把一个大图不失信息的变成一个小图,把多余的空白节点去掉。所谓不失信息,就是不改变其拓扑结构。举例如下:
在这里插入图片描述
右边的图看起来把左边的图进行了压缩,但是他破坏了图本来的结构。我们一点点来看。

  1. 1这个块,本来不在边界上,而在新的图中,它在边界上,也就是说,本来它右边还有空间,可是到了新的图上,他右边没有空间了。
  2. 2这个块,本来跟3这个块是不相连的,新的图上他们相连了,也就是本来二和三中间的空白块可以走出包围,到了新图上给堵上了。
  3. 3这个块,本来在边界上,新图还在边界,这可以。
    所以二维图进行离散化的时候,首先要考虑边界,然后要考虑行与行、列与列之间的空白关系。
    首先对于边界情况,该节点在行的边界上,新图中依然要在边界上。
    第二,对于空白关系的处理如下
    在这里插入图片描述
    最后一句话的意思就是如果两行或者列之间的空白超过1的话,就让他们的插值为2,也就是把多行空白合并为一行空白。

2. 方法2

第二个方法是叫限制部署的bfs。
就是知道只有200个block,如果source和target不能碰面的话,只能是这些block把source或者target包围起来了。我们就看这些block个块最多可以包围的块。不需要遍历全部的块就可以获得答案。
最多的包围数量如下,也就是(n + 1)n / 2。
在这里插入图片描述
在这里插入图片描述

3、代码

方法一的。方法二的没有实现。

class Solution {
public:
    bool isEscapePossible(vector<vector<int>>& blocked, vector<int>& source, vector<int>& target) {
        int dx[4] = {0,1,0,-1};
        int dy[4] = {1,0,-1,0};

        vector<int> row, col;
        unordered_map<int,int> rm,cm;

        // 1. 将所有可能用到的坐标全部进行离散化
        // 1.1 因为要保持图中各个节点的相对位置,所以将所有行列分开存储,按照位置关系分别进行离散化,而不是一起离散化
        for(auto b : blocked)
        {
            row.push_back(b[0]);
            col.push_back(b[1]);
        }
        row.push_back(source[0]);
        col.push_back(source[1]);
        row.push_back(target[0]);
        row.push_back(target[1]);

        // 1.2 去重,一个原数对应一个压缩后的数,不能一个原数能对应多个压缩后的数
        sort(row.begin(),row.end());
        sort(col.begin(),col.end());
        row.erase(unique(row.begin(),row.end()),row.end());
        col.erase(unique(col.begin(),col.end()),col.end());

        // 1.3 离散化
        // rid\cid代表当前离散化后的值,这里是要确定开始的位置
        // 边界为 0和1e6 - 1
        // 首先被压缩后的图也是要有边界的。下界还是0,上界就看最大到哪了
        // 如果原来边界上有值,那么我们还是要让它在边界上,否则就从1开始
        int rid = (row[0] == 0 ? 0 : 1);
        rm[row[0]] = rid;
        for(int i = 1; i < row.size(); i ++) 
        {
            rid += (row[i] == row[i - 1] + 1 ? 1 : 2);
            rm[row[i]] = rid;
        }
        // 如果1e6 - 1这个边界上有节点,那么压缩后还应该是边界,否则就不应该作为边界而给rid ++
        if(row.back() != 1e6 - 1) rid ++;

        int cid = (col[0] == 0 ? 0 : 1);
        cm[col[0]] = cid;
        for(int i = 1; i < col.size(); i ++)
        {
            cid += (col[i] == col[i - 1] + 1 ? 1 : 2);
            cm[col[i]] = cid;
        }
        if(col.back() != 1e6 - 1) cid ++;

        // 2. 重建压缩图
        vector<vector<int> > g(rid + 1, vector<int>(cid + 1) );
        for(auto t : blocked) g[rm[t[0]]][cm[t[1]]] = 1;
        int sr = rm[source[0]];
        int sc = cm[source[1]];
        int tr = rm[target[0]];
        int tc = cm[target[1]];

        // 3. 在压缩图中进行bfs
        queue<pair<int,int> > q;
        q.push({sr,sc});
        while(!q.empty())
        {
            auto t = q.front();
            q.pop();
            for(int i = 0; i < 4; i ++)
            {
                int nx = t.first + dx[i];
                int ny = t.second + dy[i];
                if(nx < 0 || nx > rid || ny < 0 || ny > cid) continue;
                if(g[nx][ny] == 1) continue;
                if(nx == tr && ny == tc) return true;
                q.push({nx,ny});
            }
        }
        return false;
    };
};

4、总结

  1. 对于一个大大的图上,关键的信息少少的时候,可以使用离散化。
  2. 二维离散化该如何进行。
  3. 限制次数的bfs,了解一下

参考资料

官方题解

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值