题目大意:
从S走到E,其中W是墙壁不能走,D是门,必须找到钥匙K才能经过门,求能从S走到E所用的最少步数
方法一:
从s到e有两种走法。第一种是不经过d,从s到达e。第二种是先从s到k,再从k到e。bfs函数实现求出从点(x1,y1)到(x2,y2)之间的最短距离。求出s到e的距离se,s到k的距离sk,k到e的距离ke。比较se和sk+ke的大小取较小者即为答案。显然计算se,sk,ke的时候要注意se不能经过d,sk也不能经过d,只有ke可以经过d。bfs函数的参数是两个点(x1,y1)和(x2,y2)。当(x1,y1)是点s的时候不能经过d。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=510;
int h,w;
int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};
char g[N][N];//存图
int d[N][N];//记录bfs中每个点到起点(x1,y1)的距离
pair<int,int>S;//大写S记录s的位置,以便判断bfs函数的参数点(x1,y1)是不是s
signed bfs(pair<int,int>s,pair<int,int>e)//bfs函数的参数是两个点(x1,y1)和(x2,y2),功能是求出这两个点之间的距离
{
queue<pair<int,int>>q;
memset(d,-1,sizeof d);//每次bfs前初始化d数组
q.push(s);
d[s.first][s.second]=0;
while(q.size())
{
auto t=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int x=t.first+dx[i],y=t.second+dy[i];
if(g[x][y]!='W'&&d[x][y]==-1)//这个点不是墙并且之前没有走过这个点
{
if(s==S&&g[x][y]=='D') continue;//当(x1,y1)是点s的时候不能经过d。
else
{
d[x][y]=d[t.first][t.second]+1;
if(e==make_pair(x,y)) return d[x][y];//到达了(x2,y2)
q.push({x,y});
}
}
}
}
return -1;//无法从(x1,y1)到达(x2,y2)
}
signed main()
{
pair<int,int>s,e,di,k;//记录四个点s,e,d,k为了避免重命名将d命名为di
cin>>h>>w;
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
{
cin>>g[i][j];
if(g[i][j]=='S') S=s={i,j};
else if(g[i][j]=='E') e={i,j};
else if(g[i][j]=='K') k={i,j};
else if(g[i][j]=='D') di={i,j};
}
int sk=bfs(s,k);
int ke=bfs(k,e);
int se=bfs(s,e);
int res=1e9;
if(se!=-1) res=se;//能从s到达e
if(sk!=-1&&ke!=-1) res=min(res,sk+ke);//能从s到k再从k到e
if(res==1e9) res=-1;//即无法从s到达e,又无法能从s到k再从k到e
cout<<res;
}
方法二:
这题不同于普通的bfs,因为找到k后可以走之前走过的点。用一个结构体存每个扩展得到的点,存储点的坐标,已经走了多少步和目前是否含有钥匙。每次用t扩展点时,如果t已经有了钥匙,那么扩展得到的点也含有钥匙。如果t不含有钥匙,那么扩展得到的点也不含钥匙。找到钥匙前,走过的地方标记为D。找到钥匙后,走过的地方标记为W,这样能避免重复走,同时处理了有无钥匙的情况。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 510;
char g[N][N];//地图
int stx, sty;//s的坐标
int dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
struct Node//用结构体存每个点
{
int x, y, step;//当前点的坐标,距离起点的距离
int flag = 0; //标记当前是否已经有了钥匙;
};
queue<Node> r;
int bfs()
{
r.push({stx,sty,0,0});//起点s入队
g[stx][sty] = 'D';//初始没有钥匙,初始点变成D
while(r.size())
{
auto t= r.front();
r.pop();
int x = t.x, y = t.y, step = t.step;
for(int i = 0; i < 4; i++)//可以向四个方向扩展四个点
{
int tx = x+dir[i][0], ty = y+dir[i][1];
if(g[tx][ty] == 'W') continue;//这个点是墙壁,无法通过
else if(g[tx][ty] == 'E') return step+1;//这个点是终点
else if(g[tx][ty] == 'K')//这个点是钥匙
{
g[tx][ty] = 'W';//这个点变成墙壁
r.push({tx,ty,step+1,1});//已经有了钥匙
}
else if(g[tx][ty] == 'D')//这个点是门
{
if(!t.flag) continue;//上一个点t没有钥匙,无法通过
r.push({tx,ty,step+1,1});//上一个点t已经有了钥匙,可以通过
g[tx][ty] = 'W';//这个的地方变成墙壁
}
else if(g[tx][ty] == '.')//这个点是空地
{
if(t.flag)//上一个点t已经有了钥匙
{
r.push({tx,ty,step+1,1});
g[tx][ty] = 'W';//这个的点变成墙壁
}
else//上一个点还没有钥匙
{
r.push({tx,ty,step+1,0});
g[tx][ty] = 'D';//这个的点变成门
}
}
}
}
return -1;
}
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
cin>>g[i][j];
if(g[i][j] == 'S')
{
stx=i;
sty=j;
}
}
cout << bfs();
}