Key Task
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1824 Accepted Submission(s): 772
The result is that some first-graders have often di?culties finding the right way to their classes. Therefore, the Student Union has developed a computer game to help the students to practice their orientation skills. The goal of the game is to find the way out of a labyrinth. Your task is to write a verification software that solves this game.
The labyrinth is a 2-dimensional grid of squares, each square is either free or filled with a wall. Some of the free squares may contain doors or keys. There are four di?erent types of keys and doors: blue, yellow, red, and green. Each key can open only doors of the same color.
You can move between adjacent free squares vertically or horizontally, diagonal movement is not allowed. You may not go across walls and you cannot leave the labyrinth area. If a square contains a door, you may go there only if you have stepped on a square with an appropriate key before.
Note that it is allowed to have
- more than one exit,
- no exit at all,
- more doors and/or keys of the same color, and
- keys without corresponding doors and vice versa.
You may assume that the marker of your position (“*”) will appear exactly once in every map.
There is one blank line after each map. The input is terminated by two zeros in place of the map size.
One step is defined as a movement between two adjacent cells. Grabbing a key or unlocking a door does not count as a step.
1 10
*........X
1 3
*#X
3 20
####################
#XY.gBr.*.Rb.G.GG.y#
####################
0 0
Sample Output
Escape possible in 9 steps.
The poor student is trapped!
Escape possible in 45 steps.
题目大意,你在一个n*m的迷宫里边,*代表你的起点,X代表终点,小写字母代表钥匙,能够开对应大写字母的门,问最小步数。
分析:因为有多把钥匙,我们直接枚举各种情况比较复杂,(虽然这个题只有四把钥匙直接枚举所有情况也可以,但是如果26把呢?所以还是用状压吧~)所以我们考虑状态压缩+BFS的解法。
解题过程:
我们设置一个01串表示对于这种钥匙的有无,对于四把钥匙,我们设置长为4的01串,对于其中含义,1表示有这种钥匙,0表示没有这种钥匙,举例说明:
0001表示有第一把钥匙,0101表示有第三把和第一把钥匙。因为存01串比较麻烦,所以状压的过程也是把01串变成10进制值的过程。
对于状态压缩的初始化我们搞定了,之后就要讨论钥匙的拿取和能否通过门的判断:
1、对于钥匙的拿取,如果遇到小写字母(也就是走到了钥匙所在的格子),我们要怎样在状态上修改呢?因为二进制值的计算法则,其实我们直接加上(1<<第几把钥匙)即可。假如现在01串的值为5:0101,现在走到了第四把钥匙所在的格子,这个时候状态改变为:5+(1<<4)=13即可。这个时候不妨算一下13的二进制数来证明:1101.结论成立。
2、对于门的打开,如果遇到了大写字母(也就是走到了门所在的格子),我们要怎样判断有没有这种门对应的钥匙呢?假设我们现在的状态值为13,对应01串为:1101,这个时候我们遇到了第四个门,我们看01串是完全可以看出来是确实有第四个门的钥匙的,而且我们也可以把十进制的数转成二进制数然后来判断,但是那样太麻烦了,我们只需要用1101&1000即可,因为与运算的特殊性,只有两个1的时候才能出 1,其他情况全出0,那么我们用这个特性就可以直接来判断我们需要的第几把钥匙的有无,也就是:
当前状态值&(1<<第几个门),如果与值为0表示没有这个门对应的钥匙,否则表示有这个门对应的钥匙、
注意的点:
1、不要重复拿有过的钥匙,如果在这里没有判断的话,测测这组数据就知道会wa了~:
5 5
*rrGX
rrrrG
rrrrr
GrrrG
XGrGX
2、对于位运算的优先级规则记得不是很清楚的小伙伴门一定要在位运算的过程中都加上括号保证数据结果的正确。
3、枚举情况一定要全面。
AC代码:
#include<stdio.h>
#include<queue>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;//duo ba yaoshi de qingkuang mei chuli
struct zuobiao
{
int x,y,output,tmp;
} now,nex;
char a[115][115];
int vis[115][115][1<<5];
int fx[4]= {0,0,1,-1};
int fy[4]= {1,-1,0,0};
char men[4]= {'B','Y','R','G'};
char key[4]= {'b','y','r','g'};
int n,m;
void bfs(int x,int y)
{
memset(vis,0,sizeof(vis));
now.x=x;
now.y=y;
now.tmp=now.output=0;
queue<zuobiao >s;
vis[x][y][0]=1;
s.push(now);
while(!s.empty())
{
now=s.front();
s.pop();
if(a[now.x][now.y]=='X')
{
printf("Escape possible in %d steps.\n",now.output);
return ;
}
for(int i=0; i<4; i++)
{
nex.x=now.x+fx[i];
nex.y=now.y+fy[i];
nex.output=now.output+1;
nex.tmp=now.tmp;
if(nex.x>=0&&nex.x<n&&nex.y>=0&&nex.y<m&&a[nex.x][nex.y]!='#')
{
if(isupper(a[nex.x][nex.y])&&a[nex.x][nex.y]!='X')
{
for(int j=0; j<4; j++)
{
if(a[nex.x][nex.y]==men[j])
{
if((nex.tmp&(1<<j)))
{
if(vis[nex.x][nex.y][nex.tmp]==0)
{
vis[nex.x][nex.y][nex.tmp]=1;
s.push(nex);
}
break;
}
}
}
}
else if(islower(a[nex.x][nex.y]))
{
for(int j=0; j<4; j++)
{
if(a[nex.x][nex.y]==key[j])
{
if((nex.tmp&(1<<j))==0)
{
nex.tmp+=(1<<j);
}
if(!vis[nex.x][nex.y][nex.tmp])
{
vis[nex.x][nex.y][nex.tmp]=1;
s.push(nex);
}
}
}
}
else
{
if(vis[nex.x][nex.y][nex.tmp]==0)
{
vis[nex.x][nex.y][nex.tmp]=1;
s.push(nex);
}
}
}
}
}
printf("The poor student is trapped!\n");
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0)break;
int sx,sy;
for(int i=0; i<n; i++)
{
scanf("%s",a[i]);
for(int j=0; j<m; j++)
{
if(a[i][j]=='*')
{
sx=i;
sy=j;
}
}
}
bfs(sx,sy);
}
}