迷宫寻宝(一)(nyoj 82)

29 篇文章 0 订阅

迷宫寻宝(一)

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述

一个叫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

这题要注意想要打开一个门,要先收集齐这个门所有的钥匙.

我的方法是广搜,我做这个题的时候遇到的问题就是  有一类门,搜索到的时候钥匙还没有集齐,但是在后面的搜索过程中钥匙可以集齐的.那么如果遇到这样的门开怎么办.

我刚开始想的办法是将这类的门记录到一个数组中去,等搜索完一遍之后,再在第一遍搜索的基础上从这个数组记录的门的位置再开始搜索.但是问题是,如果一个地图中有很多这一类的门的话,那么就要进行很多次搜索,效率很低,而且我按照这个思路提交的代码还不正确= =.

后来请教了学长. 采用的方法是需要提前记录 门的位置和每个门所需要的钥匙数量.还要有个数组记录上面说的那一类门,这样,当我搜到一个门的时候,看看所需要的钥匙是不是收集齐了,如果是入队列.  如果不是,将门记录到数组中.   每次搜到钥匙就看看对应的门的钥匙是不是齐了,如果齐了并且门之前被搜到过,就把门直接放到队列里,如果不是钥匙计数加1.


#include <stdio.h>
#include <string.h>  
#include <queue>
#define SIZE 25
using namespace std;

struct node
{
	int x, y;
};

node st;
queue<node>q;
char map[SIZE][SIZE];
int m, n;//行和列
int key[10];//记录每个门有几个钥匙
node add[10];//记录每个门的地址
bool non[10];//记录搜索过程中搜到的但是钥匙还没有找齐的门
int s[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

int bfs(node start)
{
	int i, t;
	node e;
	q.push(start);
	map[start.x][start.y] = 'X';//访问过后标记为墙
	while (!q.empty())
	{
		e = q.front();
		q.pop();
		for(i = 0; i < 4; i++)
		{
			node e1;
			int xx, yy;
			xx = e.x + s[i][0];
			yy = e.y + s[i][1];
			e1.x = xx;
			e1.y = yy;
			if(map[xx][yy] != 'X' && xx < m && xx >= 0 && yy < n && yy >= 0)
			{
				if(map[xx][yy] == 'G')
				{
					return 1;
				}
				else if(map[xx][yy] >= 'a' && map[xx][yy] <= 'e')
				{//遇到钥匙,就计数,判断是否有某个门的钥匙已经全部集齐了(可以召唤神龙了)而且之前访问过了的
					t = map[xx][yy] - 'a';
					key[t] --;
					q.push(e1);
					map[xx][yy] = 'X';

					if(key[t] == 0 && non[t] == 1)
					{//non为1表示此门之前访问过但是当时钥匙没收集齐
						node e2 = {add[t].x, add[t].y};
						q.push(e2);
						non[t] = 0;
					}
				}
				else if(map[xx][yy] >= 'A' && map[xx][yy] <= 'E')
				{
					t = map[xx][yy] - 'A';
					if(key[t] == 0)
					{//所有钥匙都拿到了
						q.push(e1);
						map[xx][yy] = 'X';
					}
					else
					{//钥匙没有收集齐,将non数组中对应的位置置1
						non[t] = 1;
					}
				}
				else if(map[xx][yy] == '.')
				{
					q.push(e1);
					map[xx][yy] = 'X';
				}
			}
		}
	}
	return 0;
}

int main (void)
{
	int i, j;
	while (scanf("%d %d", &m, &n))
	{
		if(m == 0 && n == 0)
			return 0;
		memset(map, 0, sizeof(map));
		memset(key, 0, sizeof(key));
		memset(add, 0, sizeof(add));
		memset(non, 0, sizeof(non));
		while(!q.empty())
		{
			q.pop();
		}
		int t;
		for(i = 0; i < m; i++)
		{
			scanf("%s", map[i]);
			for(j = 0; j < n; j++)
			{
				if(map[i][j] >= 'a' && map[i][j] <= 'e')
				{
					t = map[i][j] - 'a';
					key[t]++;
				}
				if(map[i][j] == 'S')
				{
					st.x = i;
					st.y = j;
				}
				if(map[i][j] >= 'A' && map[i][j] <= 'E')
				{
					t = map[i][j] - 'A';
					node ex = {i, j};
					add[t] = ex;//记录门的坐标
				}
			}
		}
	
		if(bfs(st) == 1)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

随手搬几个测试数据~~(其实是讨论区看到的= =)

5 5
e.b.S
XXA.E
GX.Xc
C...X
XXXBX
YES

5 5
ecb.S
XaX.E
GX.Xc
CA...
XXXcX
YES

5 5
AEe.S
cXX.E
XXXXc
CAA..
GXXcX
YES

5 5
Aae.S
cXX.E
XXXXc
CAAE.
GXXcX
YES

1 2
SG
YES

1 3
SXG
NO





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值