题目描述
输入样例
5 4 2 3
1 1
5 4
3 3
5 3
2 4
输出样例
3
1
3
AC Code
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll Lim = 100005;
const int maxn = 505;
struct xy{
int x,y,s;
};
bool vis[maxn][maxn];
int s[maxn][maxn] = {0};
int xe[Lim][2] = {0};
int n,m,a,b;
queue<xy> q;
void pusher(int x,int y){
xy t;
t.x = x,t.y = y,t.s = 0;
q.push(t);
vis[x][y] = true;
}
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
void bfs(){
xy t;
while(!q.empty()){
t = q.front();q.pop();
for(int i=0;i<4;i++){
int nx = t.x + dx[i],ny = t.y + dy[i];
if((nx>=1&&nx<=n)&&(ny>=1&&ny<=m)&&!vis[nx][ny]){
xy t1;
t1.x = nx,t1.y = ny,t1.s = t.s + 1;
q.push(t1);
s[nx][ny] = t1.s;
vis[nx][ny] = true;
}
}
}
}
int main()
{
memset(vis,false,sizeof(vis));
cin>>n>>m>>a>>b;
for(int i=1;i<=a;i++){
int x,y;
cin>>x>>y;
pusher(x,y);
}
for(int i=1;i<=b;i++){
cin>>xe[i][0]>>xe[i][1];
}
bfs();
for(int i=1;i<=b;i++){
cout<<s[xe[i][0]][xe[i][1]]<<endl;
}
return 0;
}
解释
〇本题思路来源于洛谷博主 sinsop90。
①从题意可以看出,本题的起点具有多个,并且需要同时移动。一开始我的思路是,让他们一个个通过广搜向外扩散,某点的权值(感染时间)取最小值,但很快会发现这样的答案会使所有测试点TLE,那么如何解决同时移动的问题呢?其实很简单,通过一个布尔数组vis记录某点是否已经经过,且所有初始起点在输入时同时入列即可实现。 可以想象,倘若初始起点全部入列,那么每次bfs()
操作之后,该点的下几个结点进入队尾,而对头的下一个点正是另一个初始起点,再加上vis数组判断某点是否经过,可以轻松模拟多个起始端点的同时移动。
②pusher()
函数用于将初始起点入队,不再赘述,主要展示一下bfs()
广搜函数的细节:
void bfs(){
xy t;
while(!q.empty()){
t = q.front();q.pop();
for(int i=0;i<4;i++){
int nx = t.x + dx[i],ny = t.y + dy[i];
if((nx>=1&&nx<=n)&&(ny>=1&&ny<=m)&&!vis[nx][ny]){
xy t1;
t1.x = nx,t1.y = ny,t1.s = t.s + 1;
q.push(t1);
s[nx][ny] = t1.s;
vis[nx][ny] = true;
}
}
}
}
👆可以注意到,此处的bfs()
和先前的广搜不同,使广搜停止的条件与图遍历相同,那就是队空。注意,一定不要在while循环内部先判断该点是否已经经过,因为一开始在pusher()
中已经设置初始起点为经过。
③AC。