一个叫ACM的寻宝者找到了一个藏宝图,它根据藏宝图找到了一个迷宫,这是一个很特别的迷宫,迷宫里有N个编过号的门(N<=5),它们分别被编号为A,B,C,D,E.为了找到宝藏,ACM必须打开门,但是,开门之前必须在迷宫里找到这个打开这个门所需的所有钥匙(每个门都至少有一把钥匙),例如:现在A门有三把钥匙,ACM就必须找全三把钥匙才能打开A门。现在请你编写一个程序来告诉ACM,他能不能顺利的得到宝藏。
-
输入
-
输入可能会有多组测试数据(不超过10组)。
每组测试数据的第一行包含了两个整数M,N(1<N,M<20),分别代表了迷宫的行和列。接下来的M每行有N个字符,描述了迷宫的布局。其中每个字符的含义如下:
.表示可以走的路
S:表示ACM的出发点
G表示宝藏的位置
X表示这里有墙,ACM无法进入或者穿过。
A,B,C,D,E表示这里是门,a,b,c,d,e表示对应大写字母的门上的钥匙。
注意ACM只能在迷宫里向上下左右四个方向移动。
最后,输入0 0表示输入结束。
输出
- 每行输出一个YES表示ACM能找到宝藏,输出NO表示ACM找不到宝藏。 样例输入
-
4 4 S.X. a.X. ..XG .... 3 4 S.Xa .aXB b.AG 0 0
样例输出
-
YES NO
好恶心的一道题。。改了好长时间,每一步都反复模拟反复斟酌,结果最后发现是maze[in.x][in.y]写成了maze[v.x][v.y]。。。。。哎。。
这个题目刚开始是纠结于它到达宝藏的时候需不需要把全部的门开开,或者把全部的钥匙拿到,事实证明我想多了。。这题后台数据貌似有点问题,不管是能到宝藏就算YES,还是拿完钥匙到达宝藏才算YES都能过= =
我就以前者的思路写的,这也是我刚开始理解的,虽然后来看了许多程序他们都是以后者= = 也不知道是谁的思维比较非主流。。。
还有一点就是钥匙锁在的点拿完后会变成“.”,而且打开后的门也会变成“.”。
好了,下面就是思路了。这里的门你第一次到达后,不一定能开开,所以此时就要留下点痕迹,意思是:这个门我能到达,但是现在还不能开,等能开了我再回来。所以每当一套钥匙收集齐以后,就要检查一下对应的门,如果被留下了痕迹(感觉好邪恶),那么就可以把门打开,即入队了。如果没有被留下,那么这个门就不一定是怎么个情况了,因为它可能是还没有被到达过,但是可以到达,也可能是根本到达不了。所以要分情况讨论了。
如果某个点是钥匙,那么入队后,就检查一下钥匙的数目,为0了,就检查对应的门,有两种情况:
1.这个门曾经被经历过,被留下了痕迹,那么直接把门入队好了。因为只要曾经能到达过,现在就还是一定能到达。
2.这门没有经历过,那么此门能不能入队还是个问题,因为不知道这扇门所在的位置能不能到达,所以这种情况不作处理。
如果某个点是门,那么有两种情况:
1.钥匙齐了。而这种情况又分为两种:(1)这个点曾经经历过,那么在钥匙刚收集齐的那一刻,这个门就已经被入队了,就不用管这种情况了。(2)这个点没被经历过,那么就直接把这个点入队。
2.钥匙还没齐。那么就暗示这种情况你是第一次到达这扇门所在的位置。(以为如果不是第一次,这扇门就不是“门”的样子了,而是已经被留下了痕迹,成了“X”)。此时就把门标记下痕迹,改成X。
所以开始的时候,设置几个数组,分别表示这扇门有没有、这扇门有几个钥匙、这扇门所在位置的横纵坐标。
与普通广搜不同的是,这里的搜索不再是单纯的“扩散”了,而是扩散到一定程度,某个点就被“传送”,即突然间另外一个地方的点就加入了。
#include <stdio.h>
#include <queue>
#include <string.h>
using namespace std;
typedef struct node
{
int x, y;
}node;
node doorpos[5];
char maze[25][25];
int vis[25][25], m, n, dirx[4] = {1,-1,0,0}, diry[4] = {0,0,1,-1}, key[5], door[5], bx, by, ex, ey;
void clr()
{
memset(key, 0, sizeof(key));
memset(vis, 0, sizeof(vis));
memset(door, 0, sizeof(door));
}
int bfs()
{
int i;
queue<node> q;
node u, v;
u.x = bx;
u.y = by;
vis[u.x][u.y] = 1;
q.push(u);
while(!q.empty())
{
u = q.front();
q.pop();
if(u.x == ex && u.y == ey)
return 1;
for(i = 0 ; i < 4 ; i++)
{
v.x = u.x + dirx[i];
v.y = u.y + diry[i];
if(v.x < 0 || v.y < 0 || v.x >= m || v.y >= n || maze[v.x][v.y] == 'X' || vis[v.x][v.y])
continue;
if(maze[v.x][v.y] >= 'a' && maze[v.x][v.y] <= 'e') //这个点是钥匙
{
q.push(v); //这个点肯定可以入队。
vis[v.x][v.y] = 1;
if(key[maze[v.x][v.y]-'a'] > 0) //如果钥匙还没找齐
key[maze[v.x][v.y]-'a']--; //钥匙数就--
if(key[maze[v.x][v.y]-'a'] == 0 && door[maze[v.x][v.y]-'a'] == 1) //如果钥匙已经拿完了并且有这个钥匙对应的门
{
node in;
in.x = doorpos[maze[v.x][v.y]-'a'].x;
in.y = doorpos[maze[v.x][v.y]-'a'].y;
if(vis[in.x][in.y] == 0 && maze[in.x][in.y] == 'X') //如果门没有入过队,并且能到达,而且钥匙已经找齐了,那么就可以把门入队了
{
vis[in.x][in.y] = 1;
q.push(in);
}
}
}
else if(maze[v.x][v.y] >= 'A' && maze[v.x][v.y] <= 'E') //这个点是门
{
if(key[maze[v.x][v.y]-'A'] > 0)
maze[v.x][v.y] = 'X';//钥匙没拿完,那就把这个点标记为 可以到达,即“痕迹”。
else if(key[maze[v.x][v.y]-'A'] == 0) //如果钥匙拿完了,那么能到达这个条件的,只有这个点是在找到所有对应的钥匙之后才第一次访问的
{
vis[v.x][v.y] = 1;
q.push(v);
}
}
else //这个点是“.”或者G
{
vis[v.x][v.y] = 1;
q.push(v);
}
}
}
return 0;
}
int main()
{
int i, j;
while(scanf("%d %d", &m, &n), m!=0||n!=0)
{
clr();
for(i = 0 ; i < m ; i++)
{
scanf("%s", maze[i]);
for(j = 0 ; j < n ; j++)
{
if(maze[i][j] == 'S')
{
bx = i;
by = j;
}
else if(maze[i][j] == 'G')
{
ex = i;
ey = j;
}
else if(maze[i][j] >= 'a' && maze[i][j] <= 'e')
key[maze[i][j]-'a']++;
else if(maze[i][j] >= 'A' && maze[i][j] <= 'E')
{
doorpos[maze[i][j]-'A'].x = i;
doorpos[maze[i][j]-'A'].y = j;
door[maze[i][j]-'A'] = 1;
}
}
}
printf(bfs()? "YES\n" : "NO\n");
}
return 0;
}