迷宫求解
求迷宫中从入口到出口的所有路径是一个经典的程序设计问题。由于计算机解迷宫时,通常用的是“穷举求解”的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止。为了保证在任何位置上都能沿原路退回,显然需要用一个后进先出的结构来保存从入口到当前位置的路径。因此,在求迷宫通路的算法中应用“栈”也就是自然而然的事了。 假设迷宫如下图所示:
假设“当前位置”指的是“在搜索过程中某一 时刻所在图中某个方块位置”,则求迷宫中一条路 径的算法的基本思想是:若当前位置"可通",则纳 入"当前路径",并继续朝“下一位置”探索,即切 换“下一位置”为“当前位置”,如此重复直至到 达出口;若当前位置“不可通”,则应顺着“来向” 退回到“前一通道块”,然后朝着除“来向”之外的其他方向继续探索;若该通道块的四周四个方块均“不可通”,则应从“当前路径”上删除该通道块。所谓“下一位置”指的是“当前位置”四周四个方向(东、南、西、北)上相邻的方块。假设以栈S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。由此,“纳入路径”的操作即为“当前位置入栈”;“从当前路径上删除前一通道块”的操作即为“出栈”。 求迷宫中一条从入口到出口的路径的算法可简单描述如下:
设定当前位置的初值为入口位置;
do
{
若当前位置可通,
则
{
将当前位置插入栈顶; // 纳入路径
若该位置是出口位置,则结束; // 求得路径存放在栈中
否则切换当前位置的东邻方块为新的当前位置;
}
否则
{
若栈不空且栈顶位置尚有其他方向未被探索,
则设定新的当前位置为: 沿顺时针方向旋转 找到的栈顶位置的下一相邻块;
若栈不空但栈顶位置的四周均不可通,
则
{
删去栈顶位置; // 从路径中删去该通道块
若栈不空,则重新测试新的栈顶位置,
直至找到一个可通的相邻块或出栈至栈空;
}
}
}while (栈不空);
源码:(普通方法)
#include
#include
#define TMalloc(type,n) (type *)malloc((n)*sizeof(type))
#define GPATH '#' //宏定义当迷宫能够走通时通路路线的图形
#define GWALL '-' //宏定义当迷宫能够走通时非通路路线的图形
/*
结构体Tcell用于表示迷宫中一格的信息
node存放在迷宫中寻找出路时当前节点的位置信息,
node_stack[]存放堆栈中的各个节点的数据
move[]存放上下左右四个方位
*/
typedef struct cell
{
int row; //存放行号
int col; //存放列号
int dir; //存放4个方位, 0为右, 1为下, 2为左, 3为上
}TCell, *TCellPtr;
int verify_mat(const char *, int*, int*);
int ** read_map(const char *, int, int);
void show_map(int ** ,int, int);
void destroy_map(int ** ,int);
int ** setup_mat(int, int);
TCellPtr setup_stack(int, int);
void search_maze(int **, int **, TCellPtr , int , int );
void show_route(int **, TCellPtr, int, int, int);
// 寻找迷宫路径函数,如果存在出路,则把出路打印出来
void search_maze(int **maze, int **mark, TCellPtr pstack, int nrow, int ncol)
{
int move[4][2] ={{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
int top = 1; // 路径堆栈的栈顶指针
int found = 0; // 标记是否找到迷宫出路,found=1表示找到出路
int orientation = 0; // orientation存放当前元素正遍历的方位,0为右, 1为下, 2为左, 3为上
int next_row, next_col; // next_row, next_col分别存放当前元素的下一个元素的行列值
TCell curcell; // 当前迷宫单元
// 将迷宫入口单元压入栈顶(注意迷宫外围增设了一层“外墙”,所以入口坐标为(1,1))
pstack[0].row = 1;
pstack[0].col = 1;
pstack[0].dir = 0;
mark[1][1] = 1; // 对迷宫入口元素进行标记,表明该单元已经走过
while(top >= 0 && !found){ // 如果栈非空(没有退回到入口单元),并且没有找到迷宫出口,则持续寻找路径
curcell = pstack[top--]; // 将栈顶元素出栈
orientation = curcell.dir; // 取栈顶元素的方向值为当前方向
// 以curcell的当前方向为起点,顺时针遍历邻居单元
while(orientation < 4 && !found){ //当4个方向没有遍历完,并且没有找到迷宫出口
next_row = curcell.row + move[orientation][0]; // 计算当前方向的邻居单元的行号
next_col = curcell.col + move[orientation][1]; // 计算当前方向的邻居单元的列号
if(next_row == nrow - 2 && next_col == ncol - 2){
found = 1; //如果邻居单元恰好为迷宫出口,将found标志置为1
}
else if(!maze[next_row][next_col] && !mark[next_row][next_col]) {
// 如果邻居单元可达,并且未被访问过
mark[next_row][next_col] = 1; // 将该单元标记为已访问
pstack[top].dir = ++orientation; // 当前元素的下一个方向赋值给当前元素
top++; //将此元素放入栈顶
pstack[top].row = next_row;
pstack[top].col = next_col;
pstack[top].dir = 0;
curcell.row = next_row;
curcell.col = next_col;
orientation = 0; //方向控制dir清零
printf("%d %d\n", next_row, next_col);
}
else
orientation++;
}// while(dir < 4 && !found)
}//while(top > -1 && !found)
//如果找到迷宫出路,则调用地图输出函数;否则给出提示告诉用户该迷宫没有出路
if(found){
show_route(maze, pstack, nrow, ncol, top);
}
else{
printf("\n 该迷宫没有出路!\n");
}
}
int main()
{
int nrow_map = 0, ncol_map = 0;
const char *filename = "map.txt";
int **maze;
int **mark;
TCellPtr pstack;
if(!verify_mat(filename, &nrow_map, &ncol_map)){
printf("请检查迷宫地图文件:文件读取失败,或地图每行的元素数目不相容\n");
exit(0);
}
// 读入地图文件
maze = read_map(filename, nrow_map, ncol_map);
// 生成迷宫访问标记矩阵
mark = setup_mat(nrow_map, ncol_map);
// 初始化路径探测堆栈
pstack = setup_stack(nrow_map, ncol_map);
search_maze( maze, mark, pstack, nrow_map, ncol_map );
// 销毁为存储迷宫地图而动态分配的内存
destroy_map(maze, nrow_map);
// 销毁为存储迷宫访问标记而动态分配的内存
destroy_map(mark, nrow_map);
// 销毁路径探测堆栈
free(pstack);
system("pause");
return 0;
}
// 校验迷宫地图
// 函数功能:从文件中读入矩阵信息,验证每一行元素个数是否相同,如果是则返回1,如果否则返回0
// 函数返回值:文件读取成功返回1,同时借助指针修改传入的两个整型参数的值为行数和列数
int verify_mat(const char *filename, int* pnrow_map, int* pncol_map)
{
int nrow_map = 0, ncol_map = 0, counter = 0;
char temp;
FILE *fp;
fp=fopen(filename, "r");
if( fp){ //如果不能打开文件,提示用户迷宫文件未准备好,并退出程序
printf( "The file 'map.txt' was opened successfully for read.\n" );
}
else{
printf( "The file 'map.txt' can not be opened!\n" );
exit(0);
}
//将迷宫矩阵的行号存入nrow_map中,列号存入ncol_map中,并检查迷宫矩阵的行列元素数是否都相等
while(1)
{
temp = fgetc(fp);
if(temp == EOF) {
// 文件读取完,跳出循环
if(counter != ncol_map){
//如果迷宫矩阵的列号不是全都相同,显示提示语,并退出程序
printf("\n 迷宫矩阵任意一列的元素个数必须相同,请修改 map.txt 中的迷宫图后再运行程序!");
return 0;
}
else{
nrow_map++;
break;
}
}
if(temp != '\n'){
counter++;
}
else{ // 读入换行符时
if( counter == ncol_map )
counter = 0;
else {
if( ncol_map == 0 ){
ncol_map = counter;
counter = 0;
}
else{
//如果迷宫矩阵的行号不是全都相同,显示提示语,并退出程序
printf("\n迷宫矩阵任意一行的元素个数必须相同,请修改 map.txt 中的迷宫图后再运行程序!");
return 0;
}
}
nrow_map++;
}
}//while(1)
*pnrow_map = nrow_map;
*pncol_map = ncol_map;
fclose(fp);
return 1; // 表示校验成功
}
// 从文件filename出发建立迷宫地图二维数组
int ** read_map(const char *filename, int row, int column)
{
int **maze, i;
char temp;
FILE *fp;
fp=fopen(filename, "r");
if( fp){ //如果不能打开文件,提示用户迷宫文件未准备好,并退出程序
printf( "The file 'map.txt' was opened successfully for read.\n" );
}
else{
printf( "The file 'map.txt' can not be opened!\n" );
exit(0);
}
// 为迷宫地图申请存储空间
maze = TMalloc(int *, row);
if(!maze){
printf( "为读入迷宫地图申请内存失败\n" );
exit(0);
}
for(i=0;i|
#include
#include
#include
using namespace std;
#define MaxSize 100
int mg[10][10] = { //定义一个迷宫,0表示通道,1表示墙
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,1,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}};
struct St //定义一个栈,保存路径
{
int i; //当前方块的行号
int j; //当前广场的列号
int di; //di是下一可走方位的方位号
} St[MaxSize]; //定义栈
int top = -1; //初始化栈指针
void MgPath(int xi, int yi, int xe, int ye) //路径为从(xi,yi)到(xe,ye)
{
int i, j, di, find, k;
top++; //初始方块进栈
St[top].i = xi;St[top].j = yi;St[top].di = -1;
mg[xi][yi] = -1;
while(top>-1) //栈不为空时循环
{
i = St[top].i;j = St[top].j;di = St[top].di;
if(i==xe && j==ye) //找到了出口,输出路径
{
cout << "迷宫路径如下:/n";
for(k=0; k<=top; k++)
{
cout << "/t(" << St[k].i << "," << St[k].j << ")";
if((k+1)%5==0) cout << endl; //每输出五个方块后换一行
}
cout << endl;
return;
}
find = 0;
while(di<4 && find==0) //找下一个可走方块
{
di++;
switch(di)
{
case 0:i = St[top].i-1; j = St[top].j; break;
case 1:i = St[top].i; j = St[top].j+1; break;
case 2:i = St[top].i+1; j = St[top].j; break;
case 3:i = St[top].i; j = St[top].j-1; break;
}
if(mg[i][j]==0) find = 1; //找到通路
}
if(find==1) //找到了下一个可走方块
{
St[top].di = di; //修改原栈顶元素的di值
top++; //下一个可走方块进栈
St[top].i = i; St[top].j = j; St[top].di = -1;
mg[i][j] = -1; //避免重复走到这个方块
}
else //没有路可走,则退栈
{
mg[St[top].i][St[top].j] = 0; //让该位置变成其它路径可走方块
top--;
}
}
cout << "没有可走路径!/n";
}
int main()
{
MgPath(1,1,8,8);
}