nyoj 82 迷宫寻宝(一) 【经典BFS】



迷宫寻宝(一)

时间限制: 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
我去,快半天了。。。 
小处理:我们先统计不同种类钥匙的总数,在BFS中每找到一个钥匙,就将其对应种类钥匙的总数减一,这也就是说当某种钥匙总数为0时,说明可以打开其对应的门,否则不能。
在BFS中,有四种情况处理
1,碰到门:看该门的钥匙有没有全部找到,若钥匙全部找到 标记位置后直接进队。反之标记该门(为了情况2的处理)。
2,碰到钥匙:该种类钥匙数减一,标记位置后进队。若钥匙总数为0且其对应的门有标记(1情况中 前面查询中没能打开该门),就将该门入队,去掉对该门的标记。
3,碰到.或者G:标记后入队。
4,碰到障碍或者越界或者该位置已查找过:继续下一次搜索。
ac代码:
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct node
{
	int x, y;
};
struct rec//记录钥匙的数目 以及对应门的位置 和 标记该门是否在前面查询中 没有打开 
{
	int num, x, y, exist; 
}key[6];
int n, m;//行 列 
int sx, sy;//起点 
char map[20][20];
bool vis[20][20];//标记该位置是否查询过 
int move[4][2] = {0,1, 0,-1, 1,0, -1,0};//四个方向移动 
void getmap()
{
	int i, j;
	for(i = 0; i < 5; ++i)
	{
		key[i].num = 0;//初始化 
 	}
	for(i = 0; i < n; i++)
	{
		scanf("%s", map[i]);
		for(j = 0; j < m; j++)
		{
			if(map[i][j] == 'S')//记录起点 
			{
				sx = i;
				sy = j;
			}
			else if(map[i][j] >= 'a' && map[i][j] <= 'e')
			{
				key[map[i][j] - 'a'].num++;//记录钥匙数 
			}
			else if(map[i][j] >= 'A' && map[i][j] <= 'E')
			{
				key[map[i][j] - 'A'].x = i;//记录对应门的横轴坐标 
				key[map[i][j] - 'A'].y = j;
				key[map[i][j] - 'A'].exist = 0;//初始化  所有门都没有查询 谈不上能否打开 
			}
		} 
	}
}
int judge(int x, int y)//判断是否是障碍 或者 越界 或者该位置以及查询 
{
	if(x >= 0 && x < n && y >= 0 && y < m && map[x][y] != 'X' && !vis[x][y]) return 1;
	else return 0;
}
int BFS(int x, int y)
{
	node now, next;
	queue<node> q;
	memset(vis, false, sizeof(vis));
	now.x = x, now.y = y;
	q.push(now);
	vis[now.x][now.y] = true;
	while(!q.empty())
	{
		now = q.front();
		q.pop();
		if(map[now.x][now.y] == 'G') return 1;
		for(int k = 0; k < 4; k++)
		{
			next.x = now.x + move[k][0];
			next.y = now.y + move[k][1];
			if(judge(next.x, next.y))
			{
				char cc = map[next.x][next.y];
				if(cc >= 'a' && cc <= 'e')//钥匙 
				{
					key[cc - 'a'].num--;//找到一个 
					vis[next.x][next.y] = true;//已查找过  
					q.push(next);//进队列 
					if(key[cc - 'a'].num == 0 && key[cc - 'a'].exist == 1)//找到全部钥匙 但该门在以前查询中未能打开 
					{
						node pre = {key[cc - 'a'].x, key[cc - 'a'].y};
						q.push(pre);//现在的话可以打开  
						key[cc - 'a'].exist = 0;//去掉标记 
					} 
				}
				else if(cc >= 'A' && cc <= 'E')//门 
				{
					if(key[cc - 'A'].num == 0)//已经找到全部钥匙 且该门 没有查找过 
					{
						q.push(next);//进队列 
						vis[next.x][next.y] = true;//查找过
					} 
					else//没有找齐钥匙 标记该门 
					key[cc - 'A'].exist = 1;
				} 
				else 
				{
					q.push(next);
					vis[next.x][next.y] = true;
				}
			} 
		}
	}
	return 0;
}
int main()
{
	while(scanf("%d%d", &n, &m), n||m)
	{
		getmap();
		if(BFS(sx, sy))
		printf("YES\n");
		else
		printf("NO\n");
	}
	return 0;
} 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值