NYOJ 迷宫寻宝(一)

迷宫寻宝(一)
时间限制: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(宽度优先搜索),另一种dfs(深度优先搜索),像这种图的遍历一般建议用bfs,两种方法具体实现没法跟你讲,自己查资料或浏览以下AC代码。本题的思路是这样的,先从‘S’出发调用bfs,实时判断是否到达‘G’,找到了最好,如果未找到怎么办呢?就是得判断有没有出现过无法通过的门。所以这就需要用数组记录下之前无法通过的门,然后再遍历这些门,如果遇到一些门找到钥匙则以该门为起点继续调用bfs,如此往复。所以这是个好题。代码以注释


AC代码:

/*以下代码在下原创,如有不足多多指正*/
 
# include <stdio.h>
# include <string.h>
# include <ctype.h>
# include <stdlib.h>
# include <queue>
using namespace std;  

queue<int> q;

int m, n;
int vis[50][50];
int a[200];
int des[200];
char g[50][50];
//这个结构体的作用就是用来记录当时遇到门时,没足够钥匙的门
struct node{
	int r;
	int c;
	int flage;//先前遇到打不开的门,还未处理时记为1 ,处理后记为零
};
 
int x[4]={0, 0, 1, -1};
int y[4]={1, -1, 0, 0};
int cnt, Find, Flage;
struct node s[410];
//init()函数进行初始化,就是在外围加一圈'X' 
//这样做感觉便于操作,不用判断边界 
void init(){
	for(int i=0; i<=n+1; i++){
		g[0][i]='X';
	}
	for(int i=1; i<=m; i++){
		g[i][0]='X';g[i][n+1]='X';
	}
	for(int i=0; i<=n+1; i++){
		g[m+1][i]='X';
	}
} 
void bfs(){//宽度优先搜索函数 
	int r, c, num;
	while(!q.empty()){
	    num=q.front(); 
	    q.pop();
	    r=num/n+1;
	    c=num%n;
	    if(c==0){
	    	r--;
	    	c=n;
		}
		for(int i=0; i<=3; i++){
			if(g[r+x[i]][c+y[i]]=='G'){
				Find=1;//找到'G' 
				return;
			}
			if(!vis[r+x[i]][c+y[i]]){
				if(isalpha(g[r+x[i]][c+y[i]])){//g[i][j]为字母 
				    if(g[r+x[i]][c+y[i]]>='a'){//找到一把钥匙 
				    	a[g[r+x[i]][c+y[i]]]++;//总数加一 
				    	g[r+x[i]][c+y[i]]='.';//化为'.' ,即可行的路 
				    	vis[r+x[i]][c+y[i]]=1;
				    	q.push((r+x[i]-1)*n+c+y[i]);
					}
				    else if(g[r+x[i]][c+y[i]]<='E'){//遇到门 
				    	if(a[g[r+x[i]][c+y[i]]+32]==des[g[r+x[i]][c+y[i]]+32]){
				    		//如果钥匙数已经够可以开门 
				    		g[r+x[i]][c+y[i]]='.';
				    		vis[r+x[i]][c+y[i]]=1;
				    		q.push((r+x[i]-1)*n+c+y[i]);
						}
						else{
							//如果钥匙数还不够,记录一下门的位置 
							s[cnt].r=r+x[i];s[cnt].c=c+y[i];s[cnt].flage=1;
							cnt++;
						}
					}
				    
				}
				else{
					vis[r+x[i]][c+y[i]]=1;
					q.push((r+x[i]-1)*n+c+y[i]);
				}
			}
		}
	}
	return;
}
int main(){
	int i, j, k, b_r, b_c, temp1, temp2, no;
	while(scanf("%d%d", &m, &n)){
		getchar();
		if(m==0&&n==0){
			break;
		}
		for(i=1; i<=m; i++){
			scanf("%s", g[i]+1);
		}
		init();
		while(!q.empty()){//队列初始化清空 
			q.pop();
		}
		memset(vis, 0, sizeof(vis));
		memset(a, 0, sizeof(a));
		memset(des, 0, sizeof(des));
		cnt=0;Find=0;Flage=1;
		for(i=1; i<=m; i++){
			for(j=1; j<=n; j++){
				if(g[i][j]=='S'){
					b_r=i;b_c=j;
				}
				if(isalpha(g[i][j])&&g[i][j]>='a'){
					des[g[i][j]]++;//记录门对应的钥匙总数 
				}
			}
		}
		g[b_r][b_c]='.';
		vis[b_r][b_c]=1;
		no=(b_r-1)*n+b_c;
		q.push(no);
		bfs();//从'S'点开始调用bfs 
		while(!Find&&Flage){
			//Find!=1时表示还没找到'G';
			// Flage==1时表示之前有一个无法通过的门现在找到钥匙
			//则以这个门为起点再次调用bfs 
			Flage=0;
			for(i=0; i<=cnt-1; i++){
				temp1=s[i].r;temp2=s[i].c;
				if(g[temp1][temp2]>='A'&&g[temp1][temp2]<='E'&&s[i].flage&&a[g[temp1][temp2]+32]==des[g[temp1][temp2]+32]){
					s[i].flage=0;
					Flage=1;
					break;
				}
			}
			if(Flage){
				memset(vis, 0, sizeof(vis));
				g[temp1][temp2]='.';
				vis[temp1][temp2]=1;
				while(!q.empty()){
					q.pop();
				}
				q.push((temp1-1)*n+temp2);
				bfs();
			}
		}
		if(Find){//Find==1表示找到 
			printf("YES\n");
		}
		else{
			printf("NO\n");
		}
	}
	return 0;
}


你可能感兴趣的:(NYOJ 迷宫寻宝(一))