题意:令a,b,c回到A,B,C位置,且过程中不能相撞,求最小到达该状态时间;
分析:因为该图每二宫格必有一面墙(题中条件),可以将可以站的方格都看做节点,相邻方块建一条边,如此预处理,就能生成一个节点数不超过256的图,而且后继状态也大大减少。
再来说状态压缩,图中最多有256==2^8节点,将上面预处理生成的节点编号分别放在一个int的0-8,9-16,17-24位,该int就是图中的一个状态了,自然开1<<24int数组就OK。
AC代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXM=1500;
char mmp[20][20];
int mpp[20][20],u[MAXM],v[MAXM],next[MAXM],first[256];bool done[(1<<25)];
int vcnt,ecnt,A,B,C,a,b,c;//#define don(a,b,c) done[(a<<16)+(b<<8)+c]
struct node
{
int v[3],w;
node(int x=0,int y=0,int z=0,int ww=0)
{
v[0]=x;v[1]=y;v[2]=z;w=ww;
}
};
int bfs()
{
int i,j,k;
queue<node>q;
memset(done,0,sizeof done);
q.push(node(a,b,c,0));
done[(a<<16)+(b<<8)+c]=1;
while(!q.empty())
{
node e=q.front();q.pop();
for(i=first[e.v[0]];i!=-1;i=next[i])
for(j=first[e.v[1]];j!=-1;j=next[j])
for(k=first[e.v[2]];k!=-1;k=next[k])
{
if(v[i]==v[j]||v[i]==v[k]||v[j]==v[k]||done[(v[i]<<16)+(v[j]<<8)+v[k]])continue;//去掉移动后位置冲突的
if(v[i]==e.v[1]&&v[j]==e.v[0])continue;//前后交换位置肯定相撞了
if(v[i]==e.v[2]&&v[k]==e.v[0])continue;
if(v[j]==e.v[2]&&v[k]==e.v[1])continue;
if(v[i]==A&&v[j]==B&&v[k]==C)return e.w+1;
q.push(node(v[i],v[j],v[k],e.w+1));
done[(v[i]<<16)+(v[j]<<8)+v[k]]=1;
//printf("%d %d %d-->%d %d %d %d\n",e.v[0],e.v[1],e.v[2],v[i],v[j],v[k],e.w);
}
}
return -1;
}
void add_(int a,int b)
{
u[ecnt]=a;
v[ecnt]=b;
next[ecnt]=first[a];
first[a]=ecnt++;
}
int main()
{
int n,m,i,j,human;char s[2];
while(~scanf("%d%d%d",&m,&n,&human))
{
gets(s);
if(!m&&!n&&!human)break;
for(i=0;i<n;i++)
gets(mmp[i]);
vcnt=0;ecnt=0;
memset(first,-1,sizeof first);
memset(next,-1,sizeof next);
memset(mpp,0,sizeof mpp);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
if(mmp[i][j]!='#')
{
vcnt++;
if(mmp[i][j]=='A')A=vcnt;
if(mmp[i][j]=='B')B=vcnt;
if(mmp[i][j]=='C')C=vcnt;
if(mmp[i][j]=='a')a=vcnt;
if(mmp[i][j]=='b')b=vcnt;
if(mmp[i][j]=='c')c=vcnt;
mpp[i][j]=vcnt;
if(i&&mpp[i-1][j])add_(mpp[i-1][j],vcnt),add_(vcnt,mpp[i-1][j]);
if(j&&mpp[i][j-1])add_(mpp[i][j-1],vcnt),add_(vcnt,mpp[i][j-1]);
add_(vcnt,vcnt);//a,b,c可以选择不动
}
if(human==2)//如果人数不为3,将多的人发在其他节点中,而且该结点只能连接自己
{
C=c=++vcnt;
add_(vcnt,vcnt);
}
else if(human==1)
{
C=c=++vcnt;
add_(vcnt,vcnt);
B=b=++vcnt;
add_(vcnt,vcnt);
}
printf("%d\n",bfs());
}
return 0;
}