(1)迷宫问题
①问题描述
这是心理学中的一个经典问题。心理学家把一只老鼠从一个无顶盖的大盒子的入口处放入,让老鼠自行找到出口出来。迷宫中设置很多障碍阻止老鼠前行,迷宫唯一的出口处放有一块奶酪,吸引老鼠找到出口。
简而言之,迷宫问题是解决从布置了许多障碍的通道中寻找出路的问题。本题设置的迷宫如图1所示。
图1 迷宫示意图
迷宫四周设为墙;无填充处,为可通处。设每个点有四个可通方向,分别为东、南、西、北。左上角为入口。右下角为出口。迷宫有一个入口,一个出口。设计程序求解迷宫的一条通路。
②基本要求
-
设计迷宫的存储结构。
-
设计通路的存储结构。
-
设计求解通路的算法。
-
设计迷宫显示和通路的显示方式。
-
输入:迷宫、入口及出口可在程序中设定,也可从键盘输入。
-
输出:迷宫、入口、出口及通路路径。
③实现提示
-
存储设计
迷宫:以一个m×n的数组表示迷宫,如图3所示。数组元素0和1分别表示迷宫中的通路和障碍。迷宫四周为墙,对应的迷宫数组的边界元素均为1。
|
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
|
1 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
|
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
|
1 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
|
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
|
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
图2 迷宫存储示意图
方向:每一个可通点有4个可尝试的方向,向不同的方向前进时,目的地的坐标不同。预先把4个方向上的位移存在一个数组中。如把东、南、西、北依次编号为0、1、2、3.其增量数组move[4]如图3所示。
|
move[4] |
x |
y |
|
1 |
1 |
0 |
|
2 |
0 |
1 |
|
3 |
-1 |
0 |
|
4 |
0 |
-1 |
图3 数组move[4]
通路:通路上的每一个点有3个属性:一个横坐标属性x、一个列坐标属性y和一个方向属性d,表示其下一点的位置。如果约定尝试的顺序为东、南、西、北,则每尝试一个方向不通时,d值增1,当d增至4时,表示此位置一定不是通路上的点,从栈中去除。在找到出口时,栈中保存的就是一条迷宫通路。
-
算法设计
要寻找一条通过迷宫的路径,就必须进行试探性搜索,只要有路可走就前进一步,无路可进,换一个方向进行尝试;当所有方向均不可走时,则沿原路退回一步,重新选择未走过可走的路,如此继续,直至到达出口或返回入口(没有通路)。在探索前进路径时,需要将搜索的踪迹记录下来,以便走不通时,可沿原路返回到前一个点换一个方向再进行新的探索。后退的尝试路径与前进路径正好相反,因此可以借用一个栈来记录前进路径。
寻找通路的算法思想如下:
Setp1:入口点坐标及到达该点方向(设为-1)入栈。
Step2:当栈不空时循环执行下列操作:
2.1 栈顶元素出栈至(x,y,d)。
2.2 按预设定顺序求出下一个要试探的方向d++,执行下述操作:
2.2.1 如果方向d可走,则:
2.2.1.1 将(x,y,d)入栈。
2.2.1.2 求新点坐标,得新(x,y)。
2.2.1.3 如果新点(x,y)是终点,则试探结束;
否则,置d=0。
2.2.2 否则,试探下一个方向,d++。
Setp3:栈空,表示没有通路;否则,出栈,得到通路路径。
④测试与运行
-
当迷宫数组为图3所示时,(1,1)为入口,(4,6)为出口,则可能的通路为:
通路1:(1,1)、(1,2)、(2,2)、(3,2)、(4,2)、(4,3)、(4,4)、(4,5)、(4,6)
通路2:(1,1)、(1,2)、(2,2)、(2,3)、(2,4)、(3,4)、(4,4)、(4,5)、(4,6)
通路3:(1,1)、(1,2)、(2,2)、(2,3)、(2,4)、(2,5)、(2,6)、(3,6)、(4,6) -
重设迷宫,使得没有通路,进行测试。
⑤思考
-
若每个点有8个试探方向(东、东南、南、西南、西、西北、北、东北),如何修改程序?
-
如何求得所有通路?
-
如何求得最短通路?
#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
class Stack
{
public:
struct Node//栈里有三个变量和一个指针
{
int x;
int y;
int d;//方向
Node * next;
Node():next(NULL) {}
Node(int a,int b,int c):x(a),y(b),d(c),next(NULL) {}
};
int length;
Node * topOfStack;//栈顶指针
Stack(int a,int b ,int c)//构造函数 赋值
{
int length = 1;
Node * p = new Node(a,b,c);
topOfStack = p;
}
void push(int a,int b,int c)//入栈
{
Node * p = new Node(a,b,c);
p -> next = topOfStack;
topOfStack = p;
length ++;
}
void pop()//出栈
{
Node * p = topOfStack -> next;
delete topOfStack;
topOfStack = p;
length --;
}
void print()//打印
{
Node * p = topOfStack;
while(p !=NULL)
{
cout<<"("<<p->x<<","<<p->y<<")"<<endl;
cout<<"↑"<<endl;
p = p -> next;
}
}
bool isEmpty()//判断是不是空栈
{
if(topOfStack == NULL)return true;
else return false;
}
void Clear()//删除栈中元素
{
while(!isEmpty())
{
Node * p = topOfStack;
topOfStack = topOfStack -> next;
delete p;
p = topOfStack;
}
}
};
int main()
{
//freopen("in.cpp","r",stdin);
cout<<"please input m and n"<<endl;
int m,n;
while(cin>>m>>n)
{
int Map[m][n];
int road[m][n];//记录该节点是否被访问,0代表没访问过,1代表访问过
for(int i = 0 ; i < m ; i++)
{
for( int j = 0 ; j < n ; j++)
{
cin>>Map[i][j];
}
}
memset(road,0,sizeof(road));//对road清零处理
cout<<"entrance:"<<endl;
int entr[1][2];//入口坐标
cin>>entr[0][0]>>entr[0][1];
cout<<"exit:"<<endl;
int exit[1][2];//出口坐标
cin>>exit[0][0]>>exit[0][1];
int d[6]= {0,1,2,3,4,5}; //1左 2下 3右 4上
if((Map[entr[0][0]][entr[0][1]]==1)||(Map[exit[0][0]][exit[0][1]]==1))
{
cout<<"input error"<<endl;
continue;
}
Stack dusk(entr[0][0],entr[0][1],d[1]);//把入口坐标和方向1压入栈
road[entr[0][0]][entr[0][1]]=1;//入口坐标被访问
while((dusk.topOfStack->x != exit[0][0])||(dusk.topOfStack->y != exit[0][1]))
//当入口坐标和出口坐标的x,y有一个不一样就循环
{
if(dusk.topOfStack->d == d[1])//方向1
{
if(Map[dusk.topOfStack->x][dusk.topOfStack->y-1] == 0
&& dusk.topOfStack->x>=0
&& dusk.topOfStack->x<=m
&&dusk.topOfStack->y-1>=0
&&dusk.topOfStack->y-1<=n
&&road[dusk.topOfStack->x][dusk.topOfStack->y-1]==0)
//满足左边的节点没被访问、值为0、在n*m内,就把左边的节点压入栈
{
dusk.push(dusk.topOfStack->x,dusk.topOfStack->y-1,d[1]);
road[dusk.topOfStack->x][dusk.topOfStack->y]=1;
}
else
{
//如果不满足,方向+1
dusk.topOfStack->d ++;
}
}
if(dusk.topOfStack->d == 2)
{
if(Map[dusk.topOfStack->x+1 ][dusk.topOfStack->y] == 0
&& dusk.topOfStack->x+1>=0 && dusk.topOfStack->x+1<=m
&&dusk.topOfStack->y>=0&&dusk.topOfStack->y<=n
&&road[dusk.topOfStack->x+1][dusk.topOfStack->y]==0
)
{
dusk.push(dusk.topOfStack->x+1,dusk.topOfStack->y,d[1]);
road[dusk.topOfStack->x][dusk.topOfStack->y]=1;
}
else
{
dusk.topOfStack->d ++;
}
}
if(dusk.topOfStack->d == 3)
{
if(Map[dusk.topOfStack->x][dusk.topOfStack->y+1] == 0
&& dusk.topOfStack->x>=0 && dusk.topOfStack->x<=m
&&dusk.topOfStack->y+1>=0&&dusk.topOfStack->y+1<=n
&&road[dusk.topOfStack->x][dusk.topOfStack->y+1]==0)
{
dusk.push(dusk.topOfStack->x,dusk.topOfStack->y+1,d[1]);
road[dusk.topOfStack->x][dusk.topOfStack->y]=1;
}
else
{
dusk.topOfStack->d ++;
}
}
if(dusk.topOfStack->d == 4)
{
if(Map[dusk.topOfStack->x-1][dusk.topOfStack->y] == 0
&& dusk.topOfStack->x-1>=0 && dusk.topOfStack->x-1<=m
&&dusk.topOfStack->y>=0&&dusk.topOfStack->y<=n
&&road[dusk.topOfStack->x-1][dusk.topOfStack->y]==0)
{
dusk.push(dusk.topOfStack->x-1,dusk.topOfStack->y,d[1]);
road[dusk.topOfStack->x][dusk.topOfStack->y]=1;
}
else
{
dusk.topOfStack->d ++;
}
}
if(dusk.topOfStack->d == 5)//如果四个方向都走不通,就出栈
{
dusk.pop();
}
if(dusk.topOfStack==NULL)//如果到不了
{
cout<<"can not arrive"<<endl;
break;
}
}
dusk.print();//打印
dusk.Clear();//初始化
cout<<"please input m and n"<<endl;
}
}