您的位置:首页 > 其它

NYOJ 迷宫寻宝(一)

2016-04-02 18:30 274 查看
迷宫寻宝(一)

时间限制: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: