题目链接:点击打开链接
题目大意:
有一个迷宫,迷宫里面有对应的门,想通过门的话需要先找齐钥匙,注意钥匙可能不止一把。
解题思路:
刚看这道题的时候,感觉这道题和曾经做过的一道题很像,不过另外一道题每个门只有一把钥匙,利用状压储存状态做的。刚开始就往那方面想,但是发现钥匙的个数不确定根本没法确定状态。后来仔细看了一下,发现这道题是问最后能不能到终点,而不是到终点的最短步数,于是这道题就简单了许多。这也是这道题的关键,
解法我是用bfs写的,听说dfs也能写,这里主要说bfs。bfs的话就是先遍历所有你能到的点,在这个过程中,如果你碰到了某扇门,就将这扇门打个标记。等整个图没法走的时候,就开始在地图上暴力搜索,搜索在你打过标记的门中(注意一定是你打过标记的门)是否还有钥匙没拿到。如果钥匙都拿到了,就将这扇门的坐标加入队列。依次执行,最后判断是否能到达终点即可,注意这个题每种门只有一扇,所以也大大简化了解题过程,
这里的坑点主要有两点:
(1):在你遍历过程中达到过的门才可以被执行操作,
(2):注意整个图中可能存在没有钥匙但是却有这扇门的情况,题目虽然说至少一把钥匙,可能是想开门至少需要一把钥匙吧,也不太理解,不过这种情况是必须考虑的,整个图没有这个钥匙的话,这扇门是不能被通过的。
注意以上两点应该就没有太大问题了,不过个人觉得这个代码实现是真的恶心,细节看代码吧,
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <set>
#include <functional>
#define rank ra
#define next ne
#define pb push_back
#define hash haha
#define xlson xl,xmid,xt<<1
#define xrson xmid+1,xr,xt<<1|1
#define ylson yl,ymid,xt,yt<<1
#define yrson ymid+1,yr,xt,yt<<1|1
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int INF=1e9+7;
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
char key[5]={'a','b','c','d','e'}; //钥匙种类
char doors[5]={'A','B','C','D','E'}; //门的种类
bool dv[5]; //记录门是否在之前bfs过程中被到达过
bool ke[5]; //记录地图是否存在该钥匙
struct node
{
int x,y;
}door[5]; //记录各扇门的坐标
char ma[25][25];
bool vis[25][25]; //bfs标记数组
int n,m,sx,sy,ex,ey;
int flag;
bool check(int xx,int yy) //判断当前点是否符合要求
{
if(xx>=0&&xx<n&&yy>=0&&yy<m&&vis[xx][yy]==0&&ma[xx][yy]!='X')
{
for(int k=0;k<5;k++)
{
if(ma[xx][yy]==doors[k]) //如果能到达门 给该扇门打标记
{
dv[k]=1;
return 0;
}
}
return 1;
}
return 0;
}
void bfs()
{
memset(vis,0,sizeof(vis));
memset(dv,0,sizeof(dv));
queue<node> que;
node st;
st.x=sx;st.y=sy;
que.push(st);
vis[sx][sy]=1;
while(!que.empty())
{
node k1;
node k=que.front();
que.pop();
if(k.x==ex&&k.y==ey)
{
flag=1;
break;
}
for(int i=0;i<4;i++)
{
int xx=k.x+dx[i];
int yy=k.y+dy[i];
if(check(xx,yy))
{
k1.x=xx;k1.y=yy;
que.push(k1);
vis[k1.x][k1.y]=1;
}
}
if(que.empty()) //如果无路可走 执行下面的操作
{ //判断之前的门是否有可以开的
int fl;
for(int k=0;k<5;k++)
{
fl=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(ma[i][j]==key[k]&&vis[i][j]==0)
{
fl=0;
break;
}
}
}
if(fl&&ke[k]&&dv[k]&&vis[door[k].x][door[k].y]==0) //必须地图存在该钥匙且该扇门被到达过
{
k1.x=door[k].x;
k1.y=door[k].y;
ma[k1.x][k1.y]='.';
vis[k1.x][k1.y]=1;
que.push(k1);
}
}
}
}
}
void init() //预处理起点终点和门的坐标
{
memset(ke,0,sizeof(ke));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(ma[i][j]=='S')
{
sx=i;
sy=j;
}
if(ma[i][j]=='G')
{
ex=i;
ey=j;
}
for(int k=0;k<5;k++)
{
if(ma[i][j]==key[k]) //存在的钥匙打上标记
ke[k]=1;
if(ma[i][j]==doors[k])
{
door[k].x=i;
door[k].y=j;
}
}
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
for(int i=0;i<n;i++)
scanf(" %s",ma+i);
init();
flag=0;
bfs();
if(flag)
printf("YES\n");
else
printf("NO\n");
}
}