隐式图。“任何一个2*2子网格至少有一个障碍格”,暗示有很多障碍格且很多空格和障碍相邻。因此可以把所有的空格提取出来建立一张图,而不是临时判断向上,向下,向左,向右和原地不动这5种方案是否合法。
邻接表存储隐式图: 把所有空格子编号cnt,这样每个位置都可以用1个一维的数来表示
adj[i]:i 号格子下一步可以走几个格子。
G[i][5]:i 号格子的下一步可走的格子编号,最多5个。
如何处理鬼的数量不同?
当鬼的数量<3时,可以添加到3个。
adj[cnt] = 1; G[cnt][0] = cnt; start[2] = finals[2] = cnt++; // 令首末状态都相同
对每个状态即3个鬼的位置(3个整数),哈希映射到为一个整数处理:
int ID( int a, int b, int c ){
return (a<<16)|(b<<8)|c;
}
单向bfs
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 150;
int start[3],finals[3]; //始末状态
//邻接表存储隐式图
int adj[N]; //某个格子有多少个相连的格子
int G[N][5]; //保存某个格子可以用到哪些格子
int moved[5][2] = { {0,0},{-1,0},{0,1},{1,0},{0,-1} };
int w,h,n;
int dis[N][N][N]; //走到某个状态经过的步数
int ID( int a, int b, int c ) {
return (a<<16)|(b<<8)|c;
}
bool conflict(int a,int b,int a2,int b2){
return a2==b2||(a2==b&&b2==a); //如果两个鬼移动到同一个位置或者位置互换则冲突
}
int bfs() {
memset( dis,-1,sizeof(dis) );
queue<int> Q;
Q.push( ID(start[0],start[1],start[2]) );
while( !Q.empty() ) {
int u =Q.front(); Q.pop();
int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;
if( a==finals[0]&&b==finals[1]&&c==finals[2] ) return dis[a][b][c];
for( int i=0; i<adj[a]; ++i ) {
int a2 = G[a][i];
for( int j=0; j<adj[b]; ++j ) {
int b2 = G[b][j];
if( conflict(a,b,a2,b2) ) continue;
for( int k=0; k<adj[c]; ++k ) {
int c2 = G[c][k];
if( conflict(a,c,a2,c2)||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()
{
//freopen("f.txt","r",stdin);
while( scanf("%d %d %d\n", &w,&h,&n)==3 ) {
if( w+h+n==0 ) break;
char maze[20][20];
//输入
for( int i=0; i<h; ++i ) fgets( maze[i],20,stdin );
//printf("OKKKK");
int x[N], y[N]; // 每个空白点的横纵坐标
int num[N][N];//空白点的编号
int cnt = 0;
for( int i=0; i<h; ++i ) {
for( int j=0; j<w; ++j ) {
if( maze[i][j]!='#' ) {
x[cnt] = i; y[cnt] = j; num[i][j] = cnt;
if( maze[i][j]>='a'&&maze[i][j]<='z' ) start[maze[i][j]-'a'] = cnt;
else if( maze[i][j]>='A'&&maze[i][j]<='Z' ) finals[maze[i][j]-'A'] = cnt;
++cnt;
}
}
}
//邻接表
for( int i=0; i<cnt; ++i ) {
adj[i] = 0;
for( int j=0; j<5; ++j ) {
int x1 = x[i]+moved[j][0];
int y1 = y[i]+moved[j][1];
if( maze[x1][y1]!='#' ) {
G[i][adj[i]++] = num[x1][y1];
}
}
}
if( n<=2 ) {
adj[cnt] = 1; G[cnt][0] = cnt; start[2] = finals[2] = cnt++;
}
if( n<=1 ) {
adj[cnt] = 1; G[cnt][0] = cnt; start[1] = finals[1] = cnt++;
}
//printf("OK\n");
int ans = bfs();
printf("%d\n", ans );
}
//fclose(stdin);
return 0;
}
双向bfs
int bfs() {
memset( dis,-1,sizeof(dis) );
queue<int> Q1; // 正着
queue<int> Q2; // 反着
Q1.push( ID(start[0],start[1],start[2]) );
Q2.push( ID(finals[0],finals[1],finals[2]) );
dis[start[0]][start[1]][start[2]] = 0;
dis[finals[0]][finals[1]][finals[2]] = 1;
color[start[0]][start[1]][start[2]] = 1; //正向标为1
color[finals[0]][finals[1]][finals[2]] = 2; // 反向标为2
while( !Q1.empty()||!Q2.empty() ) {
int n1 = Q1.size();
while( n1-- ) {
int u =Q1.front(); Q1.pop();
int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;
for( int i=0; i<adj[a]; ++i ) {
int a2 = G[a][i];
for( int j=0; j<adj[b]; ++j ) {
int b2 = G[b][j];
if( conflict(a,b,a2,b2) ) continue;
for( int k=0; k<adj[c]; ++k ) {
int c2 = G[c][k];
if( conflict(a,c,a2,c2)||conflict(b,c,b2,c2) ) continue;
//在中间相遇
if( color[a2][b2][c2]==0 ) {
dis[a2][b2][c2] = dis[a][b][c] + 1;
color[a2][b2][c2] = 1;
Q1.push( ID( a2,b2,c2 ) );
}
else if( color[a2][b2][c2]==2 ) {
return dis[a2][b2][c2] + dis[a][b][c];
}
}
}
}
}
int n2 = Q2.size();
while( n2-- ) {
int u =Q2.front(); Q2.pop();
int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;
for( int i=0; i<adj[a]; ++i ) {
int a2 = G[a][i];
for( int j=0; j<adj[b]; ++j ) {
int b2 = G[b][j];
if( conflict(a,b,a2,b2) ) continue;
for( int k=0; k<adj[c]; ++k ) {
int c2 = G[c][k];
if( conflict(a,c,a2,c2)||conflict(b,c,b2,c2 ) )continue;
//在中间相遇
if( color[a2][b2][c2]==0 ) {
dis[a2][b2][c2] = dis[a][b][c] + 1;
color[a2][b2][c2] = 2;
Q2.push( ID( a2,b2,c2 ) );
}
else if( color[a2][b2][c2]==1 ) {
return dis[a2][b2][c2] + dis[a][b][c];
}
}
}
}
}
}
return -1;
}