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这个块,本来不在边界上,而在新的图中,它在边界上,也就是说,本来它右边还有空间,可是到了新的图上,他右边没有空间了。
- 2这个块,本来跟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、总结
- 对于一个大大的图上,关键的信息少少的时候,可以使用离散化。
- 二维离散化该如何进行。
- 限制次数的bfs,了解一下