最近遇到了一个较难的算法题——迷宫求解,刚把栈与队列学完,看完题面之后感觉有点想法,却又一头雾水。
有些问题看着简单但执行起来很难,而有些问题看上去很难但执行起来,更难。
迷宫求解
时间限制: 1 Sec | 内存限制: 128 MB
描述:
有一个 10 x 10 的迷宫,起点是‘S’,终点是‘E’,墙是‘#’,道路是空格。一个机器人从起点走到终点。当机器人走到一个通道块,前面已经没有路可走时,它会转向到当前面向的右手方向继续走。如果机器人能够过,则留下足迹‘*’,如果走不通,则留下标记‘!’。请你模拟机器人的走法输出最终的状态。
样例输入
##########
#S # # #
# # # #
# ## #
# ### #
# # #
# # # #
# ### ## #
## E#
##########
样例输出
##########
#**#!!!# #
# *#!!!# #
#**!!## #
#*### #
#***# #
# #***# #
# ###*## #
## ****#
##########
算法框架,基本思路啊就是,
把迷宫中的每一块当成一种元素类型一个,然后利用栈的结构特点,让机器人一步一步的探索。
如果可行,就向前迈进并留下一个脚印,同时将这一块压入栈中。如果探索到某一步时发现四周无路可去(也就是可行方格块),就退一步即元素出栈,然后再对当前位置寻找可行路径。
就是这样一步一步的探索,一点一点的入栈出栈,留下脚印、标记,直到走到终点,才停下脚步,绘制了这张迷宫路径图!
在开始描述算法框架之前,需要用到几个函数,分别是可行判断、留下脚印、迈步探索、死路标记。
可行判断
//判断下一步的可行状态
bool Pass(MazeType maze, PosType curpos){
return maze.arr[curpos.r][curpos.c]==' ' || maze.arr[curpos.r][curpos.c]=='S'
|| maze.arr[curpos.r][curpos.c]=='E';
}
留下脚印
//留下脚印
int FootPrint(MazeType &maze, PosType curpos){
maze.arr[curpos.r][curpos.c]='*';
return 1;
}
迈步探索
//探索下一步
PosType NextPos(PosType curpos, int di){
PosType pos=curpos;
switch(di)
{
case 1: pos.c++; break; //向右
case 2: pos.r++; break; //向下
case 3: pos.c--; break; //向左
case 4: pos.r--; break; //向上
}
return pos;
}
死路标记
//死路标记
int MarkPrint(MazeType &maze, PosType curpos){
maze.arr[curpos.r][curpos.c]='!';
return 1;
}
这些函数原型如果设定完成,这道题的准备工作就完成了,再利用上述的算法框架将探索过程的函数原型还原,使其各函数间分工合作,即可完成最终的路径图。
完整代码如下
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Maxsize 1010
#define Increment 100
typedef struct{
int r, c; //行号和列号
}PosType;
typedef struct{
int ord; //路径序号
PosType seat; //坐标位置
int di; //下一步方向
}SElemType; //新定义元素类型
typedef struct{
int c;
int r;
char arr[10][11];
}MazeType; //定义迷宫类型(二维字符数组)
typedef struct{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
//构建
int Init(SqStack &S){
S.base=(SElemType *)malloc(Maxsize*sizeof(SElemType));
if(!S.base) return 0;
S.top=S.base;
S.stacksize=Maxsize;
return 1;
}
//判空
int Empty(SqStack &S){
if(S.top==S.base)
return 1;
else
return 0;
}
//入栈
int Push(SqStack &S,SElemType x){
if(S.top-S.base>=S.stacksize){
S.base=(SElemType *)realloc(S.base,
(S.stacksize+Increment)*sizeof(SElemType));
if(!S.base)
return 0;
S.top=S.base+S.stacksize;
S.stacksize+=Increment;
}
*S.top++=x;
return 1;
}
//出栈
int Pop(SqStack &S, SElemType &x){
if(S.top==S.base) return 0;
x=*--S.top;
return 1;
}
//判断下一步的可行状态
bool Pass(MazeType maze, PosType curpos){
return maze.arr[curpos.r][curpos.c]==' ' || maze.arr[curpos.r][curpos.c]=='S'
|| maze.arr[curpos.r][curpos.c]=='E';
}
//留下脚印
int FootPrint(MazeType &maze, PosType curpos){
maze.arr[curpos.r][curpos.c]='*';
return 1;
}
//探索下一步
PosType NextPos(PosType curpos, int di){
PosType pos=curpos;
switch(di)
{
case 1: pos.c++; break; //右
case 2: pos.r++; break; //下
case 3: pos.c--; break; //左
case 4: pos.r--; break; //上
}
return pos;
}
//死路标记
int MarkPrint(MazeType &maze, PosType curpos){
maze.arr[curpos.r][curpos.c]='!';
return 1;
}
//在地图上探索路径
int MazePath(MazeType &maze, PosType start, PosType end){
SqStack S;
PosType curpos;
int curstep;
SElemType e;
Init(S);
curpos=start; //当前位置赋值为入口
curstep=1; //迈出第一步
do
{
if(Pass(maze, curpos)) //判通
{
FootPrint(maze, curpos); //留下脚印
e.di = 1;
e.ord = curstep;
e.seat = curpos;
Push(S, e); //入栈即加入路径
if(curpos.c == end.c &&curpos.r == end.r )
return 1;
curpos=NextPos(curpos, e.di); //先向右探索
curstep++; //迈出去
}
else
{
if(!Empty(S))
{
Pop(S, e);
while(e.di==4 && !Empty(S))
{
MarkPrint(maze, e.seat); //留下死路标记
Pop(S, e); //逐步退回
}
if(e.di<4) //对另外三个方向进行探索
{
e.di++;
Push(S, e); //入栈即已切换方向
curpos=NextPos(e.seat, e.di); //当前位置赋值为该方向的邻块
}
}
}
} while(!Empty(S));
return 0;
}
int main()
{
int i,j;
MazeType maze;
maze.r=10, maze.c=10;
for(i=0; i<maze.r; i++)
{
for(int j=0;j<maze.c; j++)
scanf("%c", &maze.arr[i][j]);
getchar(); //缓冲区吃掉回车
}
PosType start, end; //定义入口出口
for(i=0; i<10; i++) //遍历找到入口出口的坐标
{
for(j=0; j<10; j++)
{
if(maze.arr[i][j]=='S')
{
start.r=i;
start.c=j;
}
if(maze.arr[i][j]=='E')
{
end.r=i;
end.c=j;
}
}
}
MazePath(maze, start, end); //开始寻求路径
for(i=0; i<maze.r; i++) //打印最终的迷宫状态
{
for(j=0; j<maze.c; j++)
printf("%c", maze.arr[i][j]);
printf("\n");
}
return 0;
}
注释:
首先:
在迈步探索那个函数里列举了四中迈步方向,这里顺时针逆时针方向的选择是要根据题目要求设定的,观察样例可以看出,机器人是从右开始顺时针依次迈出脚步的。
第二:
在提交到平台判机时,要勾选C++,如果选择C语言,会报出编译错误,提示是函数原型里面的 & 出现问题。
最后吧
还是想说一句,这道题让我第一次接触到了 dfs(深度优先搜索),写得本萌新元气大伤,真是,一如算法深似海。。。