问题描述:用一个N 阶矩阵来表示一个迷宫,该矩阵包含元素 0 1 -2 -3分别表示通道、墙、入口、出口。从入口处开始沿着通道行经,判断是否能够到达出口。
思路分析:问题的关键包括以下几个部分
- 建立迷宫包括墙、通道、入口、出口
- 点的移动
- 走过的路径如何处理
- 当前路径不可行后如何回溯
- 终止条件
如何建立迷宫
用二维数组建立一个N阶矩阵,数组中的成员为结构体Point(表示点),每个点包含坐标(X,Y),状态Stu(表示通道、入口、墙、出口、以及BACKTRACKED标示该点是否通行过)
点的状态信息(用于建立迷宫)
enum STATU
{
END = -3,//出口
BEGIN = -2,//入口
BACKTRACKED = -1,//已经走过的路径
AVAILABLE,//可通过
WALL,//墙
SPECIAL//特殊路径
};
点的移动
从入口处开始移动,同样使用Point来表示,因为点的移动需要方向,所以在Point中加入DIRECT表示方向。该点只用来移动Point中的stu信息是没用的,同样用来建立迷宫的点中direct信息也是没用的
方向信息
enum DIRECT
{
RIGHT,//右
DOWN,//下
LEFT,//左
UP,//上
NOWAY//无路可走
};
Point
默认direct方向为右,stu状态为墙
struct Point
{
Point(int xx = 0, int yy = 0, DIRECT dd = RIGHT,STATU ss = WALL){
x = xx; y = yy; dir = dd; stu = ss;
}
Point(const Point&p){
x = p.x;
y = p.y;
dir = p.dir;
stu = p.stu;
}
Point& operator=(const Point&p){
x = p.x;
y = p.y;
dir = p.dir;
stu = p.stu;
return *this;
}
int x;
int y;
DIRECT dir;
STATU stu;
bool operator==(const Point&p){
return x == p.x&&y == p.y;
}
bool operator!=(const Point&p){
return !(*this == p);
}
};
建立迷宫
构建一个类maze表示迷宫,对于N阶段矩阵width, height都为N,因为考虑迷宫的边界问题,我们让迷宫的大小为N+2阶段矩阵,四周都用墙包起来,表示迷宫边界。
class maze{
public:
maze(int width, int height){//
this->m_width = width;
this->m_height = height;
m_maze = new Point*[width + 2];
maze_creat();
m_begin = getbegin();
//从起始点开始移动 所以把起始点的状态设置为来过
m_maze[m_begin.x][m_begin.y].stu = BACKTRACKED;
m_end = getend();
}
int m_width;
int m_height;
Point **m_maze;
Point m_begin;//起始点
Point m_end;//终点
public:
void maze_creat();//迷宫创建函数
void show();
Point getbegin();//找到起始点
Point getend();//找到终点
void setstatu();//设置状态
int min_route();//判断是否有通路
};
下面是创建迷宫的函数,手动输入迷宫信息,输入每一行的迷宫信息(0,1,-2,-3),输入必须合法 -2 -3只出现一次。
void maze::maze_creat(){//初始化迷宫 手动输入
//先把N+2的矩阵全部初始化为墙
for (int i = 0; i < m_width + 2; i++){
m_maze[i] = new Point[m_height + 2];
for (int j = 0; j < m_height + 2; j++){
m_maze[i][j] = Point(i,j);
}
}
//对输入的信息进行出来改变迷宫中对应点的状态
for (int i = 1; i <= m_width; i++){
for (int j = 1; j <= m_height ; j++){
int s = 0;
cin >> s;
switch (s)
{
case 0:
m_maze[i][j].stu = AVAILABLE;
break;
case 1:
m_maze[i][j].stu = WALL;
break;
case 2: //用于特殊用途的点 该题不会用到
m_maze[i][j].stu = SPECIAL;
break;
case -2:
m_maze[i][j].stu = BEGIN;
break;
case -3:
m_maze[i][j].stu = END;
break;
default:
m_maze[i][j].stu = SPECIAL;
break;
}
}
}
}
在迷宫中找出口
- 可行,记录信息,入栈,否则
- 换方向尝试,可行1,不可行但还有其他方向2,否则3
- 回到栈顶的点,出栈,换方向移动,回到1
- 终止条件,找到终点 或者栈为空
从起点开始,每个点有4种移动的选择(右下左上),默认从右。操作1.对于每一次可行的移动,我们用辅助的栈将其信息记录,包括坐标以及移动方向。操作2.因为默认总是从右开始移动,当向右不能移动时候(右边是墙,或者已经去过了)则换一个方向进行尝试。如果可行 操作1.记录坐标,方向,移动,如果不行,操作2.继续换方向尝试,直到所有方向都试过了仍然不能移动,说明当路径不是到达出口的路径,操作3则从栈中弹栈,找到上次移动的信息,然后操作2换一个方向尝试,可行 则操作1,不可行操作2,反复上面的操作。
终止条件
1.找到终点 返回真
2.从入口的四个方向全部尝试后,会将入口的信息出栈,此时栈为空,表示没有路径可行,返回假。
int maze::min_route(){
stack<Point>s_mz;
int row = 0;//用来表示下个点的x
int col = 0;//用来表示下个点的y
do
{
while (m_begin.dir !=NOWAY){
//只有将四个方向全部试过或者到达终点才会跳出while
row = m_begin.x;
col = m_begin.y;
switch (m_begin.dir)
{
case RIGHT:
col = m_begin.y+1;
break;
case DOWN:
row = m_begin.x+1;
break;
case LEFT:
col = m_begin.y-1;
break;
case UP:
row = m_begin.x-1;
break;
default:
break;
}
//如果下一个点可行或者是终点
if (m_maze[row][col].stu == AVAILABLE || m_maze[row][col].stu == SPECIAL ||
m_maze[row] [col].stu== END){
//把当前点的信息入栈,坐标,移动方向
s_mz.push(m_begin);
//移动到下一个点
m_begin.x = row; m_begin.y = col;
//记得要将dir的信息改回默认的right
m_begin.dir = RIGHT;
//改变达到点的状态为已经来过
m_maze[row][col] = BACKTRACKED;
if (m_begin == m_end){
//到达终点
cout << "ok" << endl;
return 1;
}
}
else
//当前方向不行,换方向,这里使用的++前面已经重载过了
++m_begin.dir;
}
//回到上次移动的点
m_begin = s_mz.top();
//删除上次的移动信息
s_mz.pop();
//将上次移动的方向++,换下一个方向继续尝试
++m_begin.dir;
} while (!s_mz.empty());//栈空则终止,即入口不能到达出口
return 0;
}