[ABC317E] Avoid Eye Contact 题解
题意简述
给定一张\(N\times M\)的地图,其中 .
表示空的可通行点,#
表示障碍,S
表示起点,G
表示终点,^
,v
,<
,>
表示观察者,其中观察者的视线范围为:从当前观察者所在的位置,按箭头所示方向,直到下一个边界或非空点间,形成的一条宽为 1 的矩形。
问从起点开始,在不暴露在观察者的视线中的情况下,到达终点,所需的最少步数。
\(2\le N,M \le 2000\)
做法分析
如果不存在观察者的话,是一个 BFS 的板题。
现在的问题是如何处理视线范围。
其实对于视线范围,我们可以理解为另一种障碍,提前预处理出每个点是否在观察者的视野内即可。
对于每个点flag[i][j][x]
分别表示第 i 行,第 j 列的点是否处在第 x 种观察者的视线内。
我们设a[i][j]
表示地图上第 i 行,第 j 列的点类型。
-
若
a[i][j] = '.'
,则flag[i][j][0] = flag[i - 1][j][0]
,若a[i][j] = 'v'
,则flag[i][j][0] = 1
。 -
若
a[i][j] = '.'
,则flag[i][j][1] = flag[i + 1][j][1]
,若a[i][j] = '^'
,则flag[i][j][1] = 1
。 -
若
a[i][j] = '.'
,则flag[i][j][2] = flag[i][j - 1][2]
,若a[i][j] = '>'
,则flag[i][j][2] = 1
。 -
若
a[i][j] = '.'
,则flag[i][j][3] = flag[i][j + 1][3]
,若a[i][j] = '<'
,则flag[i][j][3] = 1
。
最后统计时,若一个点的标记四种类型中有的一个带有标记,则该点无法通行。
预处理时间复杂度为\(O(NM)\), BFS 的时间复杂度也为\(O(NM)\),总复杂度为\(O(NM)\)。
代码
#include <bits/stdc++.h>
#define ll long long
#define inf 2000000000
using namespace std;
int n, m;
char a[2010][2010];
int dis[2010][2010];
int flag[2010][2010][4];
int stx, sty, edx, edy;
queue<pair<int,int>> q;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, 1, -1};
void bfs()
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
dis[i][j] = inf;
q.push(make_pair(stx, sty));
dis[stx][sty] = 0;
while(!q.empty())
{
auto now = q.front();
q.pop();
if(now.first == edx && now.second == edy) return;
for(int i = 0; i <= 3; i++)
{
int x = now.first + dx[i];
int y = now.second + dy[i];
if(x <= 0 || y <= 0 || x > n || y > n) continue;
if(dis[x][y] != inf) continue;
if(a[x][y] == '#') continue;
if(flag[x][y][0] || flag[x][y][1] || flag[x][y][2] || flag[x][y][3]) continue;
dis[x][y] = dis[now.first][now.second] + 1;
q.push(make_pair(x, y));
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin >> a[i][j];
if(a[i][j] == 'S')
stx = i, sty = j;
if(a[i][j] == 'G')
edx = i, edy = j;
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(a[i][j] == '.') flag[i][j][0] = flag[i - 1][j][0];
else if(a[i][j] == 'v') flag[i][j][0] = 1;
for(int i = n; i >= 1; i--)
for(int j = 1; j <= m; j++)
if(a[i][j] == '.') flag[i][j][1] = flag[i + 1][j][1];
else if(a[i][j] == '^') flag[i][j][1] = 1;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(a[i][j] == '.') flag[i][j][2] = flag[i][j - 1][2];
else if(a[i][j] == '>') flag[i][j][2] = 1;
for(int i = 1; i <= n; i++)
for(int j = m; j >= 1; j--)
if(a[i][j] == '.') flag[i][j][3] = flag[i][j + 1][3];
else if(a[i][j] == '<') flag[i][j][3] = 1;
bfs();
if(dis[edx][edy] == inf)
printf("-1");
else printf("%d", dis[edx][edy]);
return 0;
}