描述
一个叫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
思路:这道题用DFS或BFS都能写,所以在做时需要明确自己要用哪一种方法,以便于与想法的实现。我做时不是很确定用哪种方法,然后逻辑混乱了,理了很久。该题需要注意的是门的状态:1.钥匙未收集齐时门为墙。2.该种钥匙收集齐时此类门不存在。由此就有了取钥匙与开门的先后顺序,需要多次搜索。
考虑DFS:以深度为准,那么一次搜索是绝对不可能的,第一次搜索后会留下一些未搜索的区域,但已搜索的区域钥匙已拿完,进行第二次搜索开门。(1000ms的限时考虑DFS也是有点慌...)
考虑BFS:以广度为准,同样也会出现留下未搜索区域,多次BFS判断钥匙的状态,若搜一次后钥匙增加,那么就继续搜,若钥匙数量不变且未到达终点就结束搜索。
DFS代码如下:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<cstring>
using namespace std;
char map[25][25];
int m,n,fag;
int k[10],vis[25][25];
int f[4][2]={0,1,0,-1,1,0,-1,0};
void dfs(int x,int y)
{
if(fag==1) return;
if(map[x][y]=='G'&&fag==0)
{
fag=1;
return ;
}
for(int i=0;i<4;i++)
{
int x1=x+f[i][0];
int y1=y+f[i][1];
if(x1<m&&x1>=0&&y1>=0&&y1<n&&vis[x1][y1]==0&&map[x1][y1]!='X')
{
if('A'<=map[x1][y1]&&map[x1][y1]<='E'&&k[map[x1][y1]-'A']!=0)//该门钥匙还未找齐
continue;
if('a'<=map[x1][y1]&&map[x1][y1]<='e')
{
k[map[x1][y1]-'a']--;//找到一把门钥匙
map[x1][y1]='.';//变为可走
}
vis[x1][y1]=1;
dfs(x1,y1);
vis[x1][y1]=0;
}
}
}
int main()
{
while(scanf("%d%d",&m,&n)&&m!=0&&n!=0)
{
memset(vis,0,sizeof(vis));
memset(k,0,sizeof(k));
fag=0;
int x,y;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>map[i][j];
if('a'<=map[i][j]&&map[i][j]<='e')
k[map[i][j]-'a']++;//存钥匙总数
if(map[i][j]=='S')//初始位置
{
x=i,y=j;
}
}
}
vis[x][y]=1;
dfs(x,y);//第一次搜,把一次能过的门开了,收集钥匙
dfs(x,y);//第二次搜,开门搜
if(fag==1) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}//如果数据变态,可能会需要第三次搜。。。。。。
BFS代码如下:(参考https://blog.csdn.net/x_y_q_/article/details/52105545)
#include<iostream>
#include<string.h>
#include<queue>
#include<cstdio>
#include<string.h>
using namespace std;
struct node{
int x, y;
};
char map[25][25],c;
int n,m;
int need[25],have[25],x1,y1,x2,y2;
int f[4][2]={0,1,0,-1,1,0,-1,0};
int bfs()
{
int vis[25][25];
memset(vis,0,sizeof(vis));
int fag=0;//判断手中的钥匙有没有变化
queue<node>q;
node q1;
q1.x=x1,q1.y=y1;
vis[x1][y1]=1;
q.push(q1);
while(!q.empty())
{
q1=q.front();
if(q1.x==x2&&q1.y==y2) return 1;//返回1说明已经找到宝藏了
q.pop();
for(int i=0;i<4;i++)
{
int x=q1.x+f[i][0];
int y=q1.y+f[i][1];
if((x>n||x<=0||y>m||y<=0)||(vis[x][y]==1)||(map[x][y]=='X')) continue;
if(map[x][y]>='a'&&map[x][y]<='e')
{
have[map[x][y]-'a']++;
map[x][y]='.';fag=1;
}
if(map[x][y]>='A'&&map[x][y]<='E'&&have[map[x][y]-'A']<need[map[x][y]-'A'])
continue;
node v;
v.x=x,v.y=y;
vis[x][y]=1;
q.push(v);
}
}
if(fag==0) return -1;//返回-1说明手里的钥匙没有增加,这次找不到,以后也就没有找到宝藏的希望了
else return 0;//返回0说明手里的钥匙增加了,或许下一次bfs就能找到宝藏
}
int main()
{
while(cin>>n>>m&&n!=0&&m!=0)
{
getchar();
memset(have,0,sizeof(have));
memset(need,0,sizeof(need));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>c;
map[i][j]=c;
if(c>='a'&&c<='e')
need[c-'a']++;
if(c=='S')
{
x1=i,y1=j;
}
if(c=='G')
{
x2=i,y2=j;
}
}
getchar();
}
while(1)
{
if(bfs()==1)
{
cout<<"YES"<<endl;
break;
}
else if(bfs()==0)
continue;
else if(bfs()==-1)
{
cout<<"NO"<<endl;
break;
}
}
}
return 0;
}