Description
有一天,🐟ls被派遣到一个有若干障碍物的n∗n的矩阵中执行任务,他被空降到(x1,y1)这个位置,他需要移动到目的地(x2,y2),路途中不能经过有障碍物的点,注意不能越界。
这个任务看似简单,实则不然。
由于🐟ls特别喜欢吃东西,并且他的体型很均匀,是一个正方形,正方形的中心在🐟ls当前的位置,所以他一旦体型达到一定大小之后就有可能不能达到终点!请你帮助他计算一下他的体型即正方形的最长边长可以是多少,既能保证能达到终点,又能满足🐟ls的嘴,使得🐟ls能顺利抵达终点。
如果无论怎么样都达不到终点输出0。
Input
输入第一行包含一个整数n,代表矩阵的大小。
下面n行每行有n个字符,字符只能是#或者..代表障碍物和空位置。
最后一行包含四个整数x1,y1,x2,y2,代表起点和终点坐标。
保证1≤n≤1000,1≤x1,y1,x2,y2≤n。
Output
输出一个整数,代表🐟ls可以达到的最长边长是多少。
Sample Input 1
3 ... ... ... 2 2 2 2
Sample Output 1
3
Sample Input 2
3 .## ### ##. 1 1 3 3
Sample Output 2
0
Sample Input 3
3 ... ... ... 1 1 3 3
Sample Output 3
1
Sample Input 4
4 ...# ...# ...# ...# 2 2 3 2
Sample Output 4
3
题意: 有一个人在走迷宫,且他的体型可以随意变化,询问能使他走到终点的最大体型。这个人可以视作一个矩形,且矩形的中心一定是一个‘.’,因此这个矩形的边长一定为奇数。这个矩形不能越界,且不能覆盖‘#’。
分析: 容易想到可以二分体型,如果当前体型可以到达终点,则更小的体型一定可以到达终点,如果当前体型不能到达终点,则更大的体型一定不能到达终点。二分中的check函数可以写一个bfs,返回当前体型是否能到达终点。在bfs的时候,由于具有一定的体型,可能有些点就不能走了,那如何判断一个点是否可以走呢?如果得到(i, j)点为中心的矩形最大边长,这个问题就可以解决了,而这个边长同样具有二分的性质,结合二维前缀和check一下矩形范围内是否包含‘#’就可二分得到(i, j)点最大边长。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define pii pair<int, int>
#define x first
#define y second
#define inf 0x3f3f3f3f
using namespace std;
//需要注意如果这个人体型确定了,过程中就不能再变了
//满足二分性质,即边长为x可以到达终点,则小于x的边长一定也能到终点
//边长为x不能到达终点,则大于x的边长一定也无法到终点
//题目中的边长只能为奇数(按覆盖点数)
//利用二分+二维前缀和,预处理(i, j)点处最大边长
int a[1005][1005];
int x[1005][1005];//x[i][j]表示(i, j)处最大边长
char mp[1005][1005];
bool vis[1005][1005];
int _next[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
int startx, starty, endx, endy, n;
bool check(int m)
{
if(x[startx][starty] < m)
return false;
memset(vis, 0, sizeof vis);
queue<pii> q;
vis[startx][starty] = true;
q.push(make_pair(startx, starty));
while(!q.empty())
{
pii now = q.front();
q.pop();
if(now.x == endx && now.y == endy)
return true;
for(int i = 0; i <= 3; i++)
{
int nextx = now.x+_next[i][0], nexty = now.y+_next[i][1];
if(nextx >= 1 && nextx <= n && nexty >= 1 && nexty <= n && m <= x[nextx][nexty] && !vis[nextx][nexty])
{
vis[nextx][nexty] = true;
q.push(make_pair(nextx, nexty));
}
}
}
return false;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
cin >> mp[i][j];
if(mp[i][j] == '.')
a[i][j] = 1;
else
a[i][j] = 0;
a[i][j] += a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
cin >> startx >> starty >> endx >> endy;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
//二分最大外扩长度
int l = 1, r = min(min(i, n-i+1), min(j, n-j+1)), ans = -1;
while(l <= r)
{
int m = l+r>>1;
if(a[i+m-1][j+m-1]-a[i+m-1][j-m]-a[i-m][j+m-1]+a[i-m][j-m] == (2*m-1)*(2*m-1))
{
ans = m;
l = m+1;
}
else
r = m-1;
}
x[i][j] = 2*ans-1;
}
//二分最大边长
int l = 1, r = 1005, ans = -1;
while(l <= r)
{
int m = l+r>>1;
if(check(m))
{
ans = m;
l = m+1;
}
else
r = m-1;
}
if(ans == -1)
cout << 0 << endl;
else
cout << ans << endl;
return 0;
}