这道题用的是广搜,状态压缩,起初不理解状态压缩是怎么回事,一个
点可以被访问多次,怎么就还能找到最短路径?
现在有所顿悟,每到达一种新的状态就是一个新的开始(把它看作又一
个起点),那么用广搜,起点到终点,是可以找到最短路径的。
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
char grap[25][25];
int sign[25][25][5000]; //注意,这里要大于2048
int ex,ey,sx,sy;
int m,n,t,ans;
int dir[5][2]={0,0,1,0,-1,0,0,1,0,-1};
typedef struct ac{
int x,y,k,time;
}node;
void bfs(int x,int y,int time){
queue <node >q;
node next,temp;
next.x=x;
next.y=y;
next.k=0;
next.time=time;
q.push(next);
while(!q.empty()){
next=q.front();
q.pop();
if(next.time>=t) return ; //超时跳出
if(next.x==ex && next.y==ey){ //到终点
ans=next.time;
return ;
}
for(int i=1;i<=4;i++){
temp.x=next.x+dir[i][0];
temp.y=next.y+dir[i][1];
temp.k=next.k;
temp.time=next.time+1;
if(temp.x<1 ||temp.x>n || temp.y<1 || temp.y>m || grap[temp.x][temp.y]=='*') continue; //出界,撞墙
if(grap[temp.x][temp.y]>='a' && grap[temp.x][temp.y]<='z'){//钥匙,k记录钥匙
temp.k|=(1<<(grap[temp.x][temp.y]-'a'+1));
if(sign[temp.x][temp.y][temp.k]==0){ //判断这个状态是否访问过
sign[temp.x][temp.y][temp.k]=1;
q.push(temp);
}
}
else if(grap[temp.x][temp.y]>='A' && grap[temp.x][temp.y]<='Z') {//门
int k=temp.k&(1<<(grap[temp.x][temp.y]-'A'+1));
if(sign[temp.x][temp.y][temp.k]==0 && k){ //判断是否访问,及是否有该门的钥匙
sign[temp.x][temp.y][temp.k]=1;
q.push(temp);
}
}
else if(sign[temp.x][temp.y][temp.k]==0){ //普通的路,判断是否访问
sign[temp.x][temp.y][temp.k]=1;
q.push(temp);
}
}
}
}
//找到一把钥匙后就就想当与一个新的起点,因为这种状态是全新的, 从起点到终点路径依然是最短!
int main()
{
while(scanf("%d %d %d",&n,&m,&t)!=EOF){
for(int i=1;i<=n;++i){
scanf("%s",&grap[i][1]);
for(int j=1;j<=m;++j){
if(grap[i][j]=='@') sx=i,sy=j; //记录起点
else if(grap[i][j]=='^') ex=i,ey=j; //记录终点
}
}
ans=-1;
memset(sign,0,sizeof(sign)); //注意每次置零
bfs(sx,sy,0);
printf("%d\n",ans);
}
return 0;
}