题目传送门:https://ac.nowcoder.com/acm/contest/549/G
思路
把起点和终点放进同一个队列(实际上是两个队列),如果从C(或D)走过某个点并且这个点已经被D(或C)访问过了,说明他们能相遇,此时的步数即为答案。本题关键就在于某个人要走的下一个点已经被对方走过了,此时结束搜索,返回答案。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
char s[N][N];
int n,m,bx,by,ex,ey,vis1[N][N],vis2[N][N];
int dir[8][2]={0,1,0,-1,1,0,-1,0,1,1,-1,-1,1,-1,-1,1};
struct node
{
int id,x,y,cnt;//id表示是哪一个人的队列
};
queue<node>q;
int bfs()
{
q.push({1,bx,by,0});
vis1[bx][by]=1;
q.push({2,ex,ey,0});
vis2[ex][ey]=1;
while(!q.empty())
{
node tmp=q.front();q.pop();
int x=tmp.x,y=tmp.y,cnt=tmp.cnt,id=tmp.id;
if(id==1)
{
for(int i=0;i<8;i++)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&s[nx][ny]!='#'&&!vis1[nx][ny])
{
if(vis2[nx][ny])return cnt+1;
q.push({1,nx,ny,cnt+1});
vis1[nx][ny]=1;
}
}
}
else
{
for(int i=0;i<4;i++)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&s[nx][ny]!='#'&&!vis2[nx][ny])
{
if(vis1[nx][ny])return cnt+1;
q.push({2,nx,ny,cnt+1});
vis2[nx][ny]=1;
for(int i=0;i<4;i++)
{
int nnx=nx+dir[i][0];
int nny=ny+dir[i][1];
if(nnx>=1&&nnx<=n&&nny>=1&&nny<=m&&s[nnx][nny]!='#'&&!vis2[nnx][nny])
{
if(vis1[nnx][nny])return cnt+1;
q.push({2,nnx,nny,cnt+1});
vis2[nnx][nny]=1;
}
}
}
}
}
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>s[i][j];
if(s[i][j]=='C')bx=i,by=j;
else if(s[i][j]=='D')ex=i,ey=j;
}
int ans=bfs();
if(ans!=-1)printf("YES\n%d\n",ans);
else printf("NO\n");
return 0;
}
AC代码简化
可以简化一下代码,把bfs函数分成两个函数。
然后把两个vis数组合并,写成三维数组,第一个下标表示标记的是谁的路径。
把队列写成一维数组,下标表示是谁的队列。假设队列下标是u,用u^1表示u异或1,即对u取反(u=0则u^1=1,u=1则u^1=0),这样方便得到对方的队列下标。
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
char s[N][N];
int n,m,bx,by,ex,ey,vis[2][N][N];
int dir[8][2]={0,1,0,-1,1,0,-1,0,1,1,-1,-1,1,-1,-1,1};
struct node
{
int x,y;
};
queue<node>q[2];
bool bfs(int u)//u表示队列下标
//这个函数如果返回1,表示已经找到答案,结束搜索;否则继续搜索
{
int sz=q[u].size();//先记录当前队列中元素个数,之后只对这一层进行扩展搜索
while(sz--)//这里一定要注意不是while(!q.empty()),否则只会对一个队列进行不断的广搜直到无法搜索
{
node tmp=q[u].front();q[u].pop();
int x=tmp.x,y=tmp.y;
for(int i=0;i<(u==0?8:4);i++)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&s[nx][ny]!='#'&&!vis[u][nx][ny])
{
if(vis[u^1][nx][ny])return 1;//u^1即u异或1,表示对u取反;
//如果下一个点被对方走过,直接返回1,找到答案,结束搜索
q[u].push({nx,ny});
vis[u][nx][ny]=1;
}
}
}
return 0;
}
int solve()
{
q[0].push({bx,by});
vis[0][bx][by]=1;
q[1].push({ex,ey});
vis[1][ex][ey]=1;
int cnt=0;
while(!q[0].empty()||!q[1].empty())
{
cnt++;//一层一层扩展,记录步数
if(bfs(0))return cnt;
if(bfs(1))return cnt;
if(bfs(1))return cnt;//队列下标为1的人可以走两次
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>s[i][j];
if(s[i][j]=='C')bx=i,by=j;
else if(s[i][j]=='D')ex=i,ey=j;
}
int ans=solve();
if(ans!=-1)printf("YES\n%d\n",ans);
else printf("NO\n");
return 0;
}