Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……
这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方。刚开始Ignatius被关在(sx,sy)的位置,离开地牢的门在(ex,ey)的位置。Ignatius每分钟只能从一个坐标走到相邻四个坐标中的其中一个。魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去。经过若干次的尝试,Ignatius已画出整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。
Input
每组测试数据的第一行有三个整数n,m,t(2<=n,m<=20,t>0)。接下来的n行m列为地牢的地图,其中包括:
. 代表路
* 代表墙
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表带锁的门,对应的钥匙分别为a-j
a-j 代表钥匙,对应的门分别为A-J
每组测试数据之间有一个空行。
Output
针对每组测试数据,如果可以成功逃亡,请输出需要多少分钟才能离开,如果不能则输出-1
Sample Input
4 5 17
@A.B.
a*.*.
*..*^
c..b*
4 5 16
@A.B.
a*.*.
*..*^
c..b*
Sample Output
16
-1
这是道迷宫题,迷宫一般用bfs,这道题的难点在于每个点的状态难以表示出来,如果用普通的vis数组,就要开12维(10把钥匙和x,y),无论是看起来还是写起来都很烦,那么怎么表示每个点的状态呢,就要用到状态压缩。
何为状态压缩,就是将每个点有无哪把钥匙压缩到10个 二进制上(从低位到高位1表示有该钥匙,0代表无该钥匙),所以这时候的vis数组为【x】【y】【1<<]】,解决了这个难点直接bfs即可,值得一提的是,当某个点能走时要在把它放入队列的同时标记,如果先放入队列,拿出来时再标记是会tle的。
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int n,m,T;
char a[30][30];
bool vis[25][25][1<<11]; //一二维是行和列,第三维是是否有钥匙的状态,用10个二进制位表示,1位有,0为无//如果用int存会爆掉
struct point{
int x,y,time,state; //单次状态可以用int存 就是state
point(int a,int b,int c,int d) //构造函数
{
x=a;y=b;state=c;time=d;
}
point(){
}
};
int dx[4]={0,1,0,-1}; //用两个数组的配合表示四个方向的移动
int dy[4]={1,0,-1,0};
int bfs(point start,point end) //将起点和终点传入广搜
{
queue <point> que;
que.push(start);
vis[start.x ][start.y ][start.state ]=1;
while(!que.empty()){
point tp=que.front(); //tp是当前的状态
que.pop();
if(tp.x==end.x&&tp.y==end.y)
return tp.time;
for(int i=0;i<4;i++){
int x=tp.x+dx[i]; //此时的xyst是下一步的状态参数
int y=tp.y+dy[i];
int s=tp.state;
int t=tp.time;
if(vis[x][y][s]==0&&a[x][y]!='*'){ //下一步没走过且不是墙
vis[x][y][s]=1;
if(a[x][y]!='.'){ //下一步不是点,就只有钥匙和门两种情况
if(a[x][y]>='a'){
s=s|(1<<(a[x][y]-'a')); //如果是钥,标记,并且塞进队列
vis[x][y][s]=1;
que.push(point(x,y,s,t+1));
}
else
{
if(s&(1<<(a[x][y]-'A'))){ //如果是门,就判断有没有钥匙,用and实现该二进制位
que.push(point(x,y,s,t+1));
}
}
}
else{
que.push(point(x,y,s,t+1)); //如果下一步是路,就可以直接走
}
}
}
}
return -1;
}
int main()
{
while(cin>>n>>m>>T)
{
memset(vis,0,sizeof(vis));
for(int i=0;i<=n+1;i++)
for(int j=0;j<=m+1;j++)
{
a[i][j]='*'; //在外围包一层墙
}
point s,e;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
if(a[i][j]=='@')
{
s=point(i,j,0,0); //起点
a[i][j]='.';
}
if(a[i][j]=='^')
{
e=point(i,j,0,0); //终点
a[i][j]='.';
}
}
int ans=bfs(s,e); //开始广搜
if(ans==-1||ans>=T)
{
cout<<"-1"<<endl;
}
else{
cout<<ans<<endl;
}
}
return 0;
}