迷宫求解是实验心理学中的一个经典问题,心理学家把一只老鼠从一个无顶盖的大盒子的入口处赶进迷宫,迷宫中设置很多隔壁,对前进方向形成了多处障碍,心理学家在迷宫的唯一出口处放置了一块奶酪,吸引老鼠在迷宫中寻找通路以到达出口。
输入:1代表有障碍,0代表无障碍,2表示入口,3表示出口
问题一:求出一条通路,并打印路径
思想是://思路描述 借鉴了陈越老师主编的数据结构(第二版)第92页的写法,下面的广搜同样借鉴了这样的思路描述
(1)将初始入口坐标和起始方向信息放入堆栈中
(2)从堆栈中弹出位置信息,设置当前位置和当前尝试方向;
若堆栈为空而出口未找到,则迷宫没有解,程序退出
(3)在当前位置,从当前方向按顺序尝试剩余方向的可通性
如果某个方向可通,则把当前位置和下一个方向存入堆栈(因为当这个位置被弹出的时候,会从被存入的方向开始尝试)
如果可通位置是出口,则成功退出,堆栈中从栈低到栈顶的各个位置就是通路
如果可通位置不是出口,将该可通位置设为当前位置,并将第一个方向设为当前方向;再执行第三步
(4)当四个方向均不通时转第二步
/*
如果从入口到出口的话,需要从栈底到栈顶输出
所以在这里采用 从出口开始寻找入口 到最后是从栈顶到栈底输出
*/
#include<iostream>
#include<stack>
using namespace std;
/*--------坐标,以及要尝试的开始方向-------*/
struct Node{
int x,y,d;
};
int N,dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
int maze[100][100],u[100][100];
/*--------把入口坐标和出口坐标传入-------*/
void dfs(int ix,int iy,int ox,int oy);
int main()
{
int i,j,k,ix,iy,ox,oy;
cin>>N;
for(i=0;i<N;i++){
for(j=0;j<N;j++){
cin>>maze[i][j];
if(maze[i][j]==2){
ix= i;iy = j;
}
else if(maze[i][j]==3){
ox= i;oy = j;
}
}
}
dfs(ix,iy,ox,oy);
return 0;
}
void dfs(int ix,int iy,int ox,int oy)
{
//stack<Node>s;
Node tmp;
stack<Node> s;
int i,j,k,x,y,nx,ny,d;
bool find = false;
tmp.x = ox;tmp.y=oy;tmp.d = 0;
u[ox][oy] = 1;
s.push(tmp);
/*--------当栈不为空,且没有找到出口时-------*/
while(!s.empty() && !find){ //s.empty()==0
x = s.top().x;y = s.top().y;d = s.top().d;
s.pop();
//s.pop(); Node a = s.top();
while(d<4&&!find){
nx = x + dir[d][0];
ny = y + dir[d][1];
/*--------如果尝试的方向可行的话-------*/
if((nx>=0 && nx<N) && (ny>=0 && ny<N)&&!u[nx][ny]&& maze[nx][ny]!=1){
u[nx][ny] = 1;
tmp.x = x;
tmp.y = y;
tmp.d= d+1;
s.push(tmp);
/*--------如果是出口的话,但是出口是作为尝试的方向,并没有进栈-------*/
if(nx==ix&&ny == iy){
find = true;
break;
}
/*--------如果不是出口的话,就把尝试的位置设置为当前位置,并把要尝试的方向设置为0-------*/
x = nx;y = ny;d = 0;
}
/*--------如果此方向行不通的话,尝试下一个方向-------*/
else{
d++;
}
}
}
/*--------如果找到了,从栈顶到栈底依次输出-------*/
if(find){
printf("(%d,%d)\n",ix,iy);
while(!s.empty()){
printf("(%d,%d)\n",s.top().x,s.top().y);
s.pop();
}
}
else{
printf("NOT FOUND");
}
}
问题二:求出最短路径,打印路径,并显示走了几步
思路:
(1)将初始入口坐标放入队列中
(2)从队头中得到位置信息,设置当前位置;
若队列为空而出口未找到,则迷宫没有解,程序退出
(3)在当前位置,依次尝试相邻的四个方向
如果某个方向可通,则把可通位置入队,且记录可通位置的前一个点是当前位置
如果可通位置是出口,则成功退出,从出口坐标来依次寻找前一个点
循环二三步
/*
此时不难看出,当从出口寻找前一个点时,输出的是倒序,这里我们采用从出口找入口的方式
*/
#include<iostream>
#include<queue>
using namespace std;
struct Node {
int x,y;
Node(){
}
Node(int i,int j){
x = i;
y = j;
}
}mo[100][100];
int maze[100][100],u[100][100],N,dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int bfs(int ix,int iy,int ox,int oy);
int main()
{
int i,j,k,ix,iy,ox,oy;
cin>>N;
for(i=0;i<N;i++){
for(j=0;j<N;j++){
cin>>maze[i][j];
if(maze[i][j]==2){
ix= i;iy = j;
}
else if(maze[i][j]==3){
ox= i;oy = j;
}
}
}
cout<<bfs(ix,iy,ox,oy);
return 0;
}
int bfs(int ix,int iy,int ox,int oy)
{
queue<Node>q;
struct Node tmp;
int i,j,k,x,y,nx,ny,cnt = 0;
bool find = false;
tmp.x = ox;
tmp.y = oy;
q.push(tmp);
u[ox][oy] = 1;
while(!q.empty() && !find){
tmp = q.front();
x = tmp.x;
y = tmp.y;
q.pop();
for(i=0;i<4;i++){
nx = x + dir[i][0];
ny = y + dir[i][1];
if((nx>=0 && nx<N)&& (ny>=0 && ny<N)&& !u[nx][ny] && maze[nx][ny]!=1){
u[nx][ny] =1;
q.push(Node(nx,ny));
mo[nx][ny] = tmp;
//printf("(%d,%d)<-(%d,%d)\n",tmp.x,tmp.y,nx,ny);
if(nx == ix && ny == iy){
find = true;
break;
}
}
}
}
x = nx;
y = ny;
printf("(%d,%d)\n",ix,iy);
while(!(x == ox && y == oy)){
cnt++;
printf("(%d,%d)\n",mo[x][y].x,mo[x][y].y);
int xx = mo[x][y].x;
int yy = mo[x][y].y;
x = xx;
y = yy;
}
return cnt;
}