先上源代码:
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
#define ERROR -1
#define OK 1
#define FALSE 0
#define TRUE 1
typedef enum{RIGHT,DOWN,LEFT,UP}Direction;
typedef enum{YES,NO}MarkTag;
typedef struct position //迷宫中位置的坐标
{ int x;
int y;
}Position;
typedef struct
{ int order; //当前位置在路径中的序号
Position seat; //当前位置在迷宫中的坐标
Direction di; //从当前位置走到下一位置的方向
}SElemType; //栈元素的类型
typedef struct
{
SElemType *elem;
int top;
}Stack;
char maze[MAXSIZE][MAXSIZE]={
{'1','1','1','1','1','1','1','1','1','1','1'},
{'1','0','1','0','0','1','1','1','0','0','1'},
{'1','0','0','0','0','0','1','0','0','1','1'},
{'1','0','1','1','1','0','0','0','1','1','1'},
{'1','0','0','0','1','0','1','1','0','1','1'},
{'1','1','0','0','1','0','1','1','0','0','1'},
{'1','1','1','0','0','0','0','0','0','0','1'},
{'1','1','1','1','1','1','1','1','1','1','1'}
}; //用二维字符数组表示迷宫
int InitStack(Stack *S) //创建一个空栈
{
S->elem=(SElemType*)malloc(MAXSIZE*MAXSIZE*sizeof(SElemType));
if(!S->elem) return ERROR;
S->top=0; return OK;
}
int Push(Stack *S,SElemType e) //元素e入栈
{ if(S->top>=MAXSIZE*MAXSIZE) return ERROR;
S->elem[S->top++]=e; return OK;
}
int Pop(Stack *S,SElemType *e) //栈顶元素出栈,由e带回栈顶元素
{ if(S->top<=0) return ERROR;
*e=S->elem[--S->top]; return OK;
}
int Empty(Stack S) //判断栈是否为空
{ if(S.top==0) return TRUE;
else return FALSE;
}
int createMaze(Position *startpos,Position *endpos)
{ Position start,end;
printf("请输入迷宫入口的位置:");
scanf("%d%d",&start.x,&start.y);
printf("请输入迷宫出口的位置:");
scanf("%d%d",&end.x,&end.y);
*startpos=start; *endpos=end;
return OK;
} //createMaze
int canPass(Position curpos)
{ if(maze[curpos.x][curpos.y]=='0') return TRUE;
return FALSE;
} //canPass
void markPos(Position curpos,MarkTag tag) //为已经探索过的位置加标记
{ switch(tag)
{ case YES: maze[curpos.x][curpos.y]='.'; break; //路径标记
case NO: maze[curpos.x][curpos.y]='#'; break; //死胡同标记
}
}
//根据当前的位置坐标和下一步要探索的方向dir求下一步要走的位置坐标
Position nextPos(Position curpos,Direction dir)
{ Position nextpos;
switch(dir)
{ case RIGHT:nextpos.x=curpos.x ;nextpos.y =curpos.y +1; break;
case DOWN :nextpos.x=curpos.x+1 ;nextpos.y =curpos.y; break;
case LEFT :nextpos.x=curpos.x ;nextpos.y =curpos.y -1; break;
case UP :nextpos.x=curpos.x-1 ;nextpos.y =curpos.y; break;
}
return nextpos;
}
Direction nextDir(Direction dir)
{ switch(dir)
{ case RIGHT: return DOWN;
case DOWN : return LEFT;
case LEFT: return UP;
}
}
int Solve(Stack *S,Position start,Position end)
{//若迷宫中存在从入口start到出口end的通道,则求得一条存放在栈S中,并返回TRUE,若迷宫中不存在从入口start到出口end的通道,并返回FALSE
Position curpos;
SElemType e;
int curstep=1; //共用的步数
if(InitStack(S)==ERROR) return FALSE;
curpos=start;
do{
if(canPass(curpos)){ //当前位置可以通过
markPos(curpos,YES); //留下足迹
e.order=curstep;e.seat=curpos;e.di=RIGHT;
Push(S,e); //当前位置加入路径
if(curpos.x==end.x&&curpos.y==end.y) //当前位置是出口
return TRUE;
curpos=nextPos(curpos,RIGHT);
curstep++;
}
else{ //当前位置不能通过
if(!Empty(*S)){
if(Pop(S,&e)==ERROR) return FALSE;
while(e.di==UP&&!Empty(*S)){
//四个方向都试探过,没有找到通路也不能继续探索,则回溯
curpos=e.seat;markPos(curpos,NO);
if(Pop(S,&e)==ERROR) return FALSE;
}
if(e.di!=UP){ //四个方向还没有试探完
e.di=nextDir(e.di);
Push(S,e); //换下一个方向探索
curpos=nextPos(e.seat,e.di);
}
}
}
}while(!Empty(*S));
return FALSE;
}
void main()
{ Position startPos,endPos;
Stack path;
int i,j;
SElemType e;
if(createMaze(&startPos,&endPos)==ERROR) return ;
Solve(&path,startPos,endPos);
while(!Empty(path)){ //输出出口到入口的路径
Pop(&path,&e);
printf("(%d,%d)",e.seat.x,e.seat.y);
}
//输出迷宫的图形
printf("\n");
for(i=0;i<8;i++)
{for(j=0;j<11;j++)
printf("%c ",maze[i][j]);
printf("\n");
}
}
一、实验的目的和要求
1、了解栈的特征,以及顺序表在迷宫问题中的应用。
2、掌握顺序栈在迷宫问题如何实现,熟练掌握顺序栈的基本操作。
二、实验的主要内容
问题描述:在迷宫中求从入口到出口的一条简单路径。迷宫可用下图中所示的方块来表示,每个方块或者是通道(用空白方块表示)或者是墙(用带阴影的方块表示)。
三、解题思路
本实验的实验题目是在迷宫中求从入口到出口的一条简单路径。迷宫用一个二维字符数组maze来表示,用x表示纵坐标,y表示横坐标。
(1)定义探索迷宫的四个方向:RIGHT为向右,DOWN向下,LEFT向左,UP向上,用Switch语句根据当前的位置坐标来判断下一步要探索的方向求出下一步要走的位置坐标。
(2) 探索迷宫的四个方向的坐标表示:RIGHT(x,y+1), DOWN(x+1,y), LEFT(x,y-1), UP(x-1,y)。
(3)对每个方向探索时,用0表示可通过,用1表示不能通过是障碍
(4) 对探索过的位置加以标记,若可以通过则用’.’ 表示,若是死胡同则用‘#’表示。
(5)把每一个可以通过的位置坐标放入栈中,最后打印输出路径。
(6)根据二维字符数组和加标记的位置坐标,输出迷宫的图形。
四、程序调试及输出结果
请输入迷宫入口的位置:1 1
请输入迷宫出口的位置:6 9
(6,9)(6,8)(6,7)(6,6)(6,5)(5,5)(4,5)(3,5)(2,5)(2,4)(2,3)(2,2)(2,1)(1,1)
Press any key to continue
五、调试小结
在这个问题中主要运用了栈的各种基本操作,例如构造空栈,判断栈是否为空,入栈操作,出栈操作等等。在迷宫中用‘1’表示墙,用‘0’表示通道;探索过的路径用‘.’表示可通过,用‘#’表示死胡同。定义了一个二维字符数组来表示迷宫,将迷宫的入口位置的坐标,在路径中的序号及该位置走到下一个位置的方向放入栈中。在设计程序的过程中由于没有注意到行列都是从0开始的,导致输出时少了一行和一列,后来行数和列数都加1后就正确了。虽然这个程序运行后可以得到正确的实验结果,但是有时还会显示存在着一个警告(warning C4715: 'nextDir' : not all control paths return a value)就是说不是所有的控制路径都能返回一个真值。我上网查了许多的资料还是不能解决这个问题,是做这个程序设计留下的一个遗憾。通过这次的程序设计我学习到了很多的东西,明白了做一个大的程序题要用到以前学到过的很多知识,就说我的程序用的是栈的知识,还用了Switch语句和for语句这两个循环语句,用了二维字符数组的知识。迷宫问题还有许多其他的方法来解决,例如顺序表,深度优先遍历,广度优先遍历等。
找出走出迷宫的所有路径,以及最短路径
#include "stdafx.h"
#include "iostream.h"
#include "string.h"
#include "stdio.h"
double dMeans=0,dWalkLen=10000;//dMeans表示走出迷宫的方法,dWalkLen表示当前走出迷宫最少步数
char Maze[10][52]={
{"###################################################"},
{"% ## #### ### ### # ####"},
{"# ## # ### ### ###### ### ############ # # #"},
{"# ## ## ### ## ## # # ## # # ####"},
{"# # # ## ## ### # # ######### # # # ##"},
{"# # # # ## ########## #### ## # #"},
{"# ## ### ## ## ### #### ## ## # # ######### #"},
{"# # # ## ## # ## #### # # ## ####"},
{"#### ## #### #### ## # ### ## ## @"},
{"###################################################"},
}; //迷宫
int MazeFlag[10][51]; //迷宫的标志:0表示未走过,i(i=1,2,3,4)表示已经走过了,i表示方向。
int MazeMin[10][51]; //路径最小的迷宫的标志
void Walk(int nx,int ny);//走迷宫的函数,nx是列,ny是行
void PrintOut(); //打印路径及迷宫的函数,同时比较获取路径较短的行走方法
int Judge(int nx,int ny,int i);//判断在第nx列ny行向第i个方向走是否可以,可以返回1否则返回0。
//i=1表示向右,2表示向下,3表示向左,4表示向上
/*---------------------------------------------------------------------------------------------
//行走迷宫函数: void Walk (int nx,int ny)
//功能:判断是否已经走出迷宫,如果走出则打印路径,如果没有则开始逐个方向判断是否可以行走,
// 如果都不能行走,或已经返回。则退出该位置,即将该位置的标志写为0表明未走过。
//无返回值,形参nx为当前位置的列,ny为当前位置的行。
---------------------------------------------------------------------------------------------*/
void Walk(int nx,int ny)
{
if (Maze[nx][ny]=='@')//判断是否走出迷宫,@是迷宫出口标志
PrintOut(); //走出则打印出迷宫及行走路径
else //未走出迷宫
{
for (int i=1; i<=4; i++)//四个方向逐个行走,i=1向右 2向下 3向左 4向上
{
if (Judge(nx,ny,i)) //如果列为nx行为ny的位置向i方向是否可以行走
{
MazeFlag[nx][ny]=i;//将标志位置i表明该位置向i方向可行走
if (i==1) //分散处理,根据不同的i来确定下一步的位置,以便行走。
Walk(nx,ny+1);
else if (i==2)
Walk(nx+1,ny);
else if (i==3)
Walk(nx,ny-1);
else if (i==4)
Walk(nx-1,ny);
}
}
MazeFlag[nx][ny]=0;//如果4个方向都走不通,或者回朔则清空该点标志位,置为0表明未走过。
}
}
/*---------------------------------------------------------------------------------------------
//打印函数:void PrintOut()
//功能: 打印第dMeans种方法的在迷宫中的行走路径,以及通过比较找出目前行走步数最少的行走方法。
//无返回值,无形参。dMeans表示当前行走方法的种类。dCount是用来计算此种方法用了多少步。
---------------------------------------------------------------------------------------------*/
void PrintOut()
{
int nx,ny;
double dCount=0;
dMeans++;
cout<<"The "<<dMeans<<" ways is: "<<endl;
for (nx=0;nx<10;nx++)
{
for (ny=0;ny<51;ny++)
{
if (Maze[nx][ny]=='#')//#表示墙
cout<<"#";
else if (MazeFlag[nx][ny]==0)//不是墙但未走过的地方用空格表示
cout<<" ";
else //不是墙且走过的地方用*表示
{
cout<<".";
dCount++; //走一步总步数加1
}
}
cout<<endl;
}
cout<<"This way used "<<dCount<<" steps"<<endl;
if (dCount<dWalkLen)//如果此种方法的步数比以前方法中最少步数还要少,
{ //则将此种方法列为当前最少行走步数
for (nx=0;nx<10;nx++)
for(ny=0;ny<51;ny++)
MazeMin[nx][ny]=MazeFlag[nx][ny];
dWalkLen=dCount;
}
}
/*--------------------------------------------------------------------------------------------
//判断函数:int Judge(int nx,int ny,int i)
//功能: 判断当前位置(nx为列ny为行)向第i方向行走是否可以
//返回值int型 返回1表明可以,0表示不可以
--------------------------------------------------------------------------------------------*/
int Judge(int nx,int ny,int i)
{
if (i==1)//判断向右可否行走
{
if (ny<50&&(Maze[nx][ny+1]==' '||Maze[nx][ny+1]=='@')&&MazeFlag[nx][ny+1]==0)
return 1;
else
return 0;
}
else if (i==2)//判断向下可否行走
{
if (nx<9&&(Maze[nx+1][ny]==' '||Maze[nx+1][ny]=='@')&&MazeFlag[nx+1][ny]==0)
return 1;
else
return 0;
}
else if (i==3)//判断向左可否行走
{
if (ny>0&&(Maze[nx][ny-1]==' '||Maze[nx][ny-1]=='@')&&MazeFlag[nx][ny-1]==0)
return 1;
else
return 0;
}
else if (i==4)//判断向上可否行走
{
if (nx>0&&(Maze[nx-1][ny]==' '||Maze[nx-1][ny]=='@')&&MazeFlag[nx-1][ny]==0)
return 1;
else
return 0;
}
else
return 0;
}
int main(int argc, char* argv[])
{
int nx,ny,ni,nj;
cout<<"迷宫游戏: "<<endl;
for (ni=0;ni<10;ni++)//输出迷宫形状,并且找到迷宫的入口,同时将迷宫标志初始化
{
for(nj=0;nj<51;nj++)
{
cout<<Maze[ni][nj];
MazeFlag[ni][nj]=0;//将迷宫标志初始化为0表示未走过
if (Maze[ni][nj]=='%')
{
nx=ni;//迷宫入口列坐标
ny=nj;//迷宫入口行坐标
}
}
cout<<endl;
}
cout<<endl<<"入口坐标:"<<endl<<"nx= "<<nx<<" "<<"ny= "<<ny<<endl;
Walk(nx,ny);//调用行走迷宫函数,从入口处开始行走
cout<<endl<<"The MinLen way is: "<<endl;
for (nx=0;nx<10;nx++)//输出最短路径
{
for (ny=0;ny<51;ny++)
{
if (Maze[nx][ny]=='#')
cout<<"#";
else if (MazeMin[nx][ny]==0)
cout<<" ";
else
{
cout<<".";
}
}
cout<<endl;
}
cout<<"This Way used "<<dWalkLen<<" steps"<<endl;//输出最短路径总行走步数
return 0;
}