这道题给人的第一感觉就是非常繁琐,因为要考虑两个人状态以外还要考虑鬼对人的影响,一开始是想让鬼也宽搜,然后每次都去考虑这个地方的位置是否已经被鬼覆盖过。
但是我们如果注意到鬼是可以穿过墙的,也就是说我们通过曼哈顿距离就可以判断出鬼能覆盖的位置,这样我们只需要通过一个简单的计算来判定是否这个地方可以走就可以了。
那么我们要考虑就是一个普通的双向BFS,我们分别记录下这两人的初始位置,然后我们这里还要学习到一个小的技巧,那就是对于BFS的队列,我们其实是可以控制每一次出队的结点的深度,我们只要保存下现有队列的元素个数,然后让这些点出队,就可以实现这个功能,然后我么每次走到一个新的点都去判断一下这个点是不是已经被对方打上了标记,如果有,那就说明这两个人最终是相遇的。
这道BFS说难也并没有特别难,最关键的还是要多积累,把题目的思路给理顺,同诸位共勉。
#include<bits/stdc++.h>
using namespace std;
const int maxn=8e2+50;
char mp[maxn][maxn];
int n,m,gx[2],gy[2],stx,sty,edx,edy,vis[2][maxn][maxn],step=0,cnt=0;
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
struct node{
int x,y;
node(){}
node(int xx,int yy):x(xx),y(yy){}
};
queue<node> q[2];
bool check(node u) {
int x=u.x,y=u.y;
if(x<1||x>n||y<1||y>m||mp[x][y]=='X') return 0;
if(( abs(x-gx[0])+abs(y-gy[0])<=2*step )||( abs(x-gx[1])+abs(y-gy[1])<=2*step) ) return 0;
return 1;
}
int bfs(int w) {
int V=q[w].size();
while(V--) {
node u=q[w].front();q[w].pop();
if(!check(u)) continue;
for(int i=0;i<4;i++){
int nx=u.x+dx[i];
int ny=u.y+dy[i];
if(!check(node(nx,ny))) continue;
if(!vis[w][nx][ny]){
if(vis[w^1][nx][ny]) return 1;
vis[w][nx][ny]=1;
q[w].push(node(nx,ny));
}
}
}
return 0;
}
int solve() {
vis[0][stx][sty]=1,vis[1][edx][edy]=1;
q[0].push(node(stx,sty));q[1].push(node(edx,edy));
while(!q[0].empty()||!q[1].empty()) {
++step;
if(bfs(0)) return step;
if(bfs(0)) return step;
if(bfs(0)) return step;
if(bfs(1)) return step;
}
return -1;
}
void clear(){
cnt=0,step=0;
memset(vis,0,sizeof(vis));
while(!q[0].empty()) q[0].pop();
while(!q[1].empty()) q[1].pop();
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int line;
cin>>line;
while(line--) {
clear();
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) {
cin>>mp[i][j];
if(mp[i][j]=='Z')
gx[cnt]=i,gy[cnt]=j,++cnt;
if(mp[i][j]=='M')
stx=i,sty=j;
if(mp[i][j]=='G')
edx=i,edy=j;
}
cout<<solve()<<endl;
}
return 0;
}