N - 迷宫问题
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
输入
一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
输出
左上角到右下角的最短路径,格式如样例所示。
样例输入
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
样例输出
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)
思路
一道典型的迷宫问题,这里我们用广搜解决这道题。(深搜也是一样,思路不变)
如果对深搜广搜不是很了解,可以看一下这篇博客:
图的广度优先搜索(BFS)和深度优先搜索(DFS)算法解析
这道题的难点在于,输出并不是我们经常所见的最短路径长度或是能否通过迷宫,而是要求你输出具体路径。
这也是一开始难倒我的地方。
先看一下普通的BFS代码(输出最短路径值):
#define _CRT_SBCURE_NO_DEPRECATE
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const int maxn = 110;
const int INF = 0x3f3f3f3f;
using namespace std;
//万能头文件慎用!
struct node
{
int x,y,step; //x,y:当前坐标 step:当前已移动次数
} pre[10005];
int sx=0,sy=0,ex=4,ey=4; //sx,sy:起始坐标 ex,ey:终点坐标
int dx[4]= {1,-1,0,0};
int dy[4]= {0,0,1,-1};
//dx与dy数组用于我们在数组中遍历
//eg:当i=1时 x+dx[1]=x-1 y+dy[1]=y
//相当于从(x,y)移动到(x-1,y),左移一格
bool vis[10][10]; //用于标记当前位置是否可走
int BFS() //返回最短路径的值
{
queue<node>q; //用队列实现
struct node tmp,tail;
tmp.x=sx,tmp.y=sy;
tmp.step=0; //对一开始的状态初始化
q.push(tmp);
vis[sx][sy]=true; //标记起点
while(!q.empty())
{
tmp=q.front();
q.pop();
if(tmp.x==ex&&tmp.y==ey) //是否已经走到终点
return tmp.step;
for(int i=0; i<4; i++) //遍历当前坐标所有四个方向
{
int xx=dx[i]+tmp.x;
int yy=dy[i]+tmp.y;
if(xx>=0&&yy>=0&&xx<5&&yy<5&&!vis[xx][yy]) //判断这一步是否越界或是不能走
{
tail.x=xx;
tail.y=yy;
tail.step=tmp.step+1; //这一点的步数等于它的起点步数+1
vis[xx][yy]=true; //标记已走过,不走回头路!
q.push(tail);
}
}
}
return -1; //此时无法走出迷宫,输出-1
}
//这段是核心代码,建议自己在草纸上模拟一遍
//没有接触过队列的话,建议先行学习队列
int main()
{
int x;
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
{
cin>>x;
if(x)
vis[i][j]=true;
else
vis[i][j]=false;
}
//输入二维数组并初始化vis数组
//当然也可以用memset(vis,false,sizeof(vis))初始化
cout<<BFS()<<endl;
}
我们将样例输入后看一下运行结果:
对照样例输出中9个坐标点可以知道最短路径长度确实为8,但是我们却没能输出过程。
这就很屑了。
解决方案就是在node结构体里加入两个新的变量:
struct node
{
int x,y,num,last; //num:当前点的序号 last:当前点的起点序号
} pre[10005]; //用一个结构体数组存入所有点的信息
这样我们再让BFS返回最后一个点的信息,根据最后一个点的last就可以找到它的上一个点,反复弹跳后就能得到这条路上所有的点(有一个好比喻:株连九族[doge])
不多废话,我万能的「伊丽莎白」啊,上代码!
#define _CRT_SBCURE_NO_DEPRECATE
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
struct node
{
int x,y,num,last; //num:当前点的序号 last:当前点的起点序号
} pre[10005]; //用一个结构体数组存入所有走过的点信息
int sx=0,sy=0,ex=4,ey=4;
int dx[4]= {1,-1,0,0};
int dy[4]= {0,0,1,-1};
int x;
bool vis[10][10];
struct node BFS() //返回最后一个点的信息
{
int j=1; //当前点的序号
queue<node>q;
struct node tmp;
pre[1].x=sx,pre[1].y=sy,pre[1].num=1,pre[1].last=0; //起点的起点设为0
q.push(pre[1]);
vis[sx][sy]=true;
while(!q.empty())
{
tmp=q.front();
q.pop();
if(tmp.x==ex&&tmp.y==ey)
return tmp; //返回最后一个点
for(int i=0; i<4; i++)
{
int xx=dx[i]+tmp.x;
int yy=dy[i]+tmp.y;
if(xx>=0&&yy>=0&&xx<5&&yy<5&&!vis[xx][yy])
{
j++; //序号+1
vis[xx][yy]=true;
pre[j].x=xx,pre[j].y=yy,pre[j].num=j,pre[j].last=tmp.num; //当前此点的last应为tmp.num
q.push(pre[j]);
}
}
}
}
int main()
{
int x;
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
{
cin>>x;
if(x)
vis[i][j]=true;
else
vis[i][j]=false;
}
struct node a=BFS();
stack<node>s; //用栈存储路径坐标
while(a.num!=NULL) //起点的起点为空
{
s.push(a);
a=pre[a.last]; //去找这个点的起点
}
while(!s.empty())
{
printf("(%d, %d)\n",s.top().x,s.top().y);
s.pop();
}
}
看完之后还是有点迷惑的话建议多看几遍,最好可以在纸上自己将整个过程模拟一遍。(很推荐,我自己也是在模拟过之后彻底起飞~)
有缘再见~