- 题意:
输入5*5矩阵,1表示墙,0表示可走。找出从左上角到右下角的最短路径所经过的坐标(输入保证有唯一解)
- 做法:
bool型二维数组vis保存输入的01矩阵,1表示墙,0表示可走。map from保存每一个经过的坐标的前驱结点坐标。queue q保存每一层扩展时的中心结点。为方便层层扩展时遍历上下左右四个方向,定义数组dx[]={0,0,-1,1},dy[]={1,-1,0,0}。
初始化:将起点加入队列
当队列非空即还有待扩展的中心结点时(while循环):
取点:取出队首结点
特判终点:判断当前中心结点是否为终点,若是,输出方案(==>后面补充print(Point))
扩展结点:对上、下、左、右相邻的结点,判断合法性(1.不出边界 2.不是墙 !vis[x][y] 3.没到过 from.find(t)==from.end()),若合法,入队列,将(合法的相邻结点,中心结点)这一映射加入from。
输出方案print(Point):输出以结点p为终点的最短路径,因为从p到起点是逆序的,可采用两种方法:①递归输出:若当前结点是起点,输出该点;若当前结点不是起点,将其前驱结点到起点的路径输出,再输出当前结点 ②用vector将p到起点所经过的坐标存起来,然后将vector倒序输出
- 注意:
1.行列编号从0开始
2.输出的特殊格式,每行中间有无空格,每行之间有无空行,结尾有无空行。
3.(0,0)是起点,最初加入时没有前驱结点。在判断合法性时,当使用条件from.find(t)==from.end()判断是否到达过时,会把(0,0)判断为没有到达过,这样就会给(0,0)制造一个前驱结点。因此,在输出方案时,递归判断是否为起点,使用 “当前结点是否为(0,0)” 而不是 “from.find(t)==from.end()"
- 总结:
对于要求输出方案的bfs,需要使用map保存路径,这样,vis只需要承担保存墙的功能,可以用一个bool型二维数组。如果需要计数步数,可以在输出路径的过程中计数,也可以把vis改为int型数组,在寻找路径的过程计数。
代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<map>
using namespace std;
struct Point{
int a;
int b;
Point(int _a,int _b):a(_a),b(_b){}
Point(){}
//比较
bool operator<(const Point &p)const{
return a!=p.a ? a<p.a : b<p.b;
}
};
const int maxn=10;
bool vis[maxn][maxn];//1代表墙
map<Point,Point> from;
queue<Point> q;
int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};
int m=5,n=5;//行数列数
void print(Point &p){
// if(from.find(p)!=from.end()){ 无法终止
if(!(p.a==0&&p.b==0)){
print(from[p]);
printf(p.a==4&&p.b==4 ? "(%d, %d)":"(%d, %d)\n",p.a,p.b);
}else{
printf("(%d, %d)\n",p.a,p.b);
}
}
void bfs(int sx,int sy,int tx,int ty)
{
//初始化
Point p(sx,sy);
Point t;
q.push(p);
while(!q.empty()){
//取出队首
p=q.front(); q.pop();
//特判终点
if(p.a==tx&&p.b==ty){
print(p);
break;
}
//扩展结点
for(int i=0;i<4;i++){
int x=p.a+dx[i],y=p.b+dy[i];
t=Point(x,y);
//判合法 加队列
//1.不出边界 2.不是墙 3.没走过
if(x>=0&&x<m&&y>=0&&y<=n&&!vis[x][y]&&from.find(t)==from.end()){
q.push(t);
from[t]=p;
}
}
}
}
int main()
{
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++){
cin>>vis[i][j];
}
}
bfs(0,0,4,4);
return 0;
}