问题:从迷宫的入口找到通向出口的路径。
算法:为了描述迷宫的布局,将定义迷宫的数组m[][]设为全局变量以减少形参传递。另外还需要一个结构体来描述迷宫足迹的坐标,定义如下:
struct Maze_Location
{ int x; //行坐标
int y; //列坐标};
int cur_num为记录通道的编号变量,每当有一个可走的通道时,cur_num就加1,最终找到一条路径的时候,该路径的呈现方式是1,2,3,4............依次按编号连成的一条线。刚开始还需设定入口和出门的坐标,并且入口坐标对应的块应该(确切的说是必须)为通道块,因此先将该坐标对应的位置上赋值为1,即cur_num当前的初始编号。为迷宫设定通道块和墙(通道块对应的值是-1,墙为0)程序的大体流程如下:
按(由东---北)的方向寻找路径
{
若下一步是通道块,则
{
编号加1,并且赋值给下一步足迹块;
若此时的这一步的坐标为出口坐标
{直接打印出路径;(一条路径已经找到!)}
否则
{递归调用该算法,但是此时的参数有变化,参数为当前这一步的坐标与当前编号(每次递归的参数都是下一步)}
编号减1;
将该块的值恢复为-1;
}
}
void FindPath(Maze_Location curpos,int cur_num)
{ int i;
Maze_Location next;
Maze_Location direction[4]={{0,1},{-1,0},{0,-1},{1,0}};//改变方向
for (i=0;i<=3;i++)
{
next.x=curpos.x+direction[i].x;
next.y=curpos.y+direction[i].y;
if (m[next.x][next.y]==-1) //若是通道块
{
m[next.x][next.y]=++cur_num; //编号+1
if (next.x!=ending.x || next.y!=ending.y)//下一步的坐标只要不是出口坐标就继续递归
FindPath(next,cur_num);
else
{
print();
printf("\n");
}
m[next.x][next.y]=-1;
cur_num--;
}
}
}
程序如下:
#include "c1.h" #define MAXLENTH 5 struct Maze_Location { int x; int y; }; typedef int Maze[MAXLENTH][MAXLENTH]; Maze m; //创建迷宫m,定义为全局变量减少形参 Maze_Location start,ending;//入口坐标、出口坐标 int cur_num=1; //初始化序号 //子函数开始 void print() { int i,j; for(i=0;i<MAXLENTH;i++) { for (j=0;j<MAXLENTH;j++) printf("%3d",m[i][j]); printf("\n"); } } void InitMaze(int k) { int i,n,j,x1,y1; for (i=0;i<MAXLENTH;i++) m[0][i]=m[i][0]=m[MAXLENTH-1][i]=m[i][MAXLENTH-1]=0;//四周值为0,0代表墙 printf("请输入你想要布置迷宫除内的墙的个数:\n"); scanf("%d",&n); for (i=1;i<MAXLENTH-1;i++) { for (j=1;j<MAXLENTH-1;j++) m[i][j]=k;//先置迷宫内部全为1,即全是通道 } printf("现在开始为迷宫布置墙,请依次输入迷宫内的墙的坐标:(行列均从0开始)\n"); for(j=1;j<=n;j++)//布n堵墙 { scanf("%d,%d",&x1,&y1); m[x1][y1]=0; } print();//打印迷宫 printf("请输入入口坐标:\n"); scanf("%d,%d",&start.x,&start.y); printf("请输入出口坐标:\n"); scanf("%d,%d",&ending.x,&ending.y); } void FindPath(Maze_Location curpos,int cur_num) { int i; Maze_Location next; Maze_Location direction[4]={{0,1},{-1,0},{0,-1},{1,0}};//注意东就是列加1行不变,而数组中对应的就是Y加1 for (i=0;i<=3;i++) { next.x=curpos.x+direction[i].x; next.y=curpos.y+direction[i].y; if (m[next.x][next.y]==-1) { m[next.x][next.y]=++cur_num; if (next.x!=ending.x || next.y!=ending.y) FindPath(next,cur_num);//只要没有到出口就继续寻找 else { print(); printf("\n"); } m[next.x][next.y]=-1;//一下两句很重要,若走的块是通道块但却没有其他出路,即只能原路返回,这时 cur_num--; //就要把之前给这个块赋的编号取消,恢复为-1 } } } void main() { InitMaze(-1); m[start.x][start.y]=1; printf("迷宫的路径如下:\n"); FindPath(start,cur_num); }
此算法很好理解,但有一点不好理解,我想了很久,即如下情形:
0 0 0 0
-1 -1 0 0
0 0 0 0
假设红色部分为现在的足迹,现在开始要往东走,我们大眼一看就知道其实不能往东走,因为下一步尽管是通道块,但是它是个死胡同,没有办法寻找下个路径了,它只能返回原来的路径。但是这在程序里怎么体现呢?这个我想了很久,就是上面的最后两句:
m[next.x][next.y]=-1;
cur_num--;
当足迹有第一个红色-1出走到下一个-1时,首先发现它是通道块,编为它进行编号(假设是序号2),如下:
0 0 0 0
1 2 0 0
0 0 0 0
发现此时的这个-1(序号2)通道除了原方向没有路可走了,那么就要返回,可是如果你直接返回的话那么下次还是会来到这里,这样就陷入死循环了,所以要取消它的编号,让它恢复为普通的通道块,序号同时也减1(用以给别的块赋值)。此时不用担心还会遇见这个死胡同了,因为这时已经到了程序的结尾了,该在红色1处的足迹上进行下一个for循环了,即换方向了。