//测试数据
/*
5 5 2
#####
#A#B#
# #
#b#a#
#####
16 4 3
################
## ########## ##
# ABCcba #
################
16 16 3
################
### ## # ##
## # ## # c#
# ## ########b#
# ## # # # #
# # ## # # ##
## a# # # # #
### ## #### ## #
## # # # #
# ##### # ## ##
#### #B# # #
## C# # ###
# # # ####### #
# ###### A## #
# # ##
################
0 0 0
*/#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
using namespace std;
const int maxs=20;
const int maxn=200;int s[3],t[3];
int deg[maxn],G[maxn][5]; //G[][]是邻接表保存,deg[i]保存第i个点有几个相连点
int dis[maxn][maxn][maxn]; //判重同时也记录距离 int ID(int a,int b,int c) //三位数变为一位数记录
{return (a<<16)|(b<<8)|c; //0|1=1;
}
bool conflict(int a,int b,int a2,int b2)
{return b2==a2||(a==b2&&b==a2); //不能穿过和重合
}
int bfs()
{queue<int> q;q.push(ID(s[0],s[1],s[2]));memset(dis,-1,sizeof(dis)); //-1表示未走过 dis[s[0]][s[1]][s[2]]=0;while(!q.empty()){int u=q.front();q.pop();int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff; //1&0=0; (A|0)&1=A; if(a==t[0]&&b==t[1]&&c==t[2]) return dis[a][b][c];for(int i=0;i<deg[a];i++){int a2=G[a][i];for(int j=0;j<deg[b];j++){int b2=G[b][j];if(conflict(a,b,a2,b2)) continue;for(int k=0;k<deg[c];k++){int c2=G[c][k];if(conflict(a,c,a2,c2)) continue;if(conflict(b,c,b2,c2)) continue;if(dis[a2][b2][c2]!=-1) continue;dis[a2][b2][c2]=dis[a][b][c]+1;q.push(ID(a2,b2,c2));}}}}return -1;
}
int main()
{int w,h,n;while(scanf("%d%d%d",&w,&h,&n)==3&&n){getchar(); //读取一个回车 char maze[20][20];for(int i=0;i<h;i++) fgets(maze[i],20,stdin); //比scanf("%c",&maze[i][j])方便或者用cin>>maze[i]但比较慢; int cnt=0,x[maxn],y[maxn],id[maxs][maxs];for(int i=0;i<h;i++)for(int j=0;j<w;j++){char ch=maze[i][j];if(ch!='#'){x[cnt]=i;y[cnt]=j; //记录每个编号的横纵坐标,方便等下记录与其连接的节点 id[i][j]=cnt; //对每个非'#'节点编号,cnt表示编号 if(islower(ch)) s[ch-'a']=cnt;else if(isupper(ch)) t[ch-'A']=cnt;cnt++;}}const int dx[]={1,-1,0, 0,0}; //可以走五步 const int dy[]={0, 0,1,-1,0};for(int i=0;i<cnt;i++){deg[i]=0;for(int dir=0;dir<5;dir++) {int nx=x[i]+dx[dir];int ny=y[i]+dy[dir];if(maze[nx][ny]!='#') G[i][deg[i]++]=id[nx][ny]; //G[i][0~deg[i]-1]是保存从i点出发可到的点; }}if(n<=2) deg[cnt]=1,G[cnt][0]=cnt,s[2]=t[2]=cnt++; //对于不到3个的,新建一个点,起点终点在同一位置且该点只能到该点,即自环 if(n<=1) deg[cnt]=1,G[cnt][0]=cnt,s[1]=t[1]=cnt++;printf("%d\n",bfs());}return 0;
}
单向稍微改编就能得到双向BFS
正着搜一个节点,反着搜一个节点 ,当遇到正反搜索都搜过的节点就表明已经找到解了。
接下来是双向BFS代码
用时从940ms变为550ms
#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
using namespace std;
const int maxs=20;
const int maxn=200;int s[3],t[3];
int deg[maxn],G[maxn][5]; //G[][]是邻接表保存,deg[i]保存第i个点有几个相连点
int dis[maxn][maxn][maxn],dis_pback[maxn][maxn][maxn]; //判重同时也记录距离 int ID(int a,int b,int c) //三位数变为一位数记录
{return (a<<16)|(b<<8)|c; //0|1=1;
}
bool conflict(int a,int b,int a2,int b2)
{return b2==a2||(a==b2&&b==a2); //不能穿过和重合
}
int bfs(queue<int>& q,int d[maxn][maxn][maxn]) {int u = q.front(); q.pop();int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;if(dis[a][b][c]!=-1&&dis_back[a][b][c]!=-1) return dis[a][b][c]+dis_back[a][b][c]; // 当遇到正反搜索都搜过的节点就表明已经找到解了 for(int i = 0; i < deg[a]; i++) {int a2 = G[a][i]; for(int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if(conflict(a, b, a2, b2)) continue;for(int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if(conflict(a, c, a2, c2)) continue;if(conflict(b, c, b2, c2)) continue;if(d[a2][b2][c2] != -1) continue;d[a2][b2][c2] = d[a][b][c]+1;q.push(ID(a2, b2, c2));}}}return -1;
}
int solve()
{queue<int> q;q.push(ID(s[0],s[1],s[2]));memset(dis,-1,sizeof(dis)); dis[s[0]][s[1]][s[2]]=0; queue<int> q_back;q_back.push(ID(t[0],t[1],t[2])); //终点出发memset(dis_back,-1,sizeof(dis_back)); dis_back[t[0]][t[1]][t[2]]=0;int ans;while(1) //正着搜一个节点,反着搜一个节点 {ans=bfs(q,dis);if(ans!=-1) return ans;ans=bfs(q_back,dis_back);if(ans!=-1) return ans;}return -1;
}
int main()
{int w,h,n;while(scanf("%d%d%d",&w,&h,&n)==3&&n){getchar(); //读取一个回车 char maze[20][20];for(int i=0;i<h;i++) fgets(maze[i],20,stdin); //比scanf("%c",&maze[i][j])方便或者用cin>>maze[i]但比较慢; int cnt=0,x[maxn],y[maxn],id[maxs][maxs];for(int i=0;i<h;i++)for(int j=0;j<w;j++){char ch=maze[i][j];if(ch!='#'){x[cnt]=i;y[cnt]=j; //记录每个编号的横纵坐标,方便等下记录与其连接的节点 id[i][j]=cnt; //对每个非'#'节点编号,cnt表示编号 if(islower(ch)) s[ch-'a']=cnt;else if(isupper(ch)) t[ch-'A']=cnt;cnt++;}}const int dx[]={1,-1,0, 0,0}; //可以走五步 const int dy[]={0, 0,1,-1,0};for(int i=0;i<cnt;i++){deg[i]=0;for(int dir=0;dir<5;dir++) {int nx=x[i]+dx[dir];int ny=y[i]+dy[dir];if(maze[nx][ny]!='#') G[i][deg[i]++]=id[nx][ny]; //G[i][0~deg[i]-1]是保存从i点出发可到的点; }}if(n<=2) deg[cnt]=1,G[cnt][0]=cnt,s[2]=t[2]=cnt++; //对于不到3个的,新建一个点,起点终点在同一位置且该点只能到该点,即自环 if(n<=1) deg[cnt]=1,G[cnt][0]=cnt,s[1]=t[1]=cnt++;printf("%d\n",solve());}return 0;
}