问题描述:给定一个M X N的迷宫图,求一条从指定入口的到出口的路径
数据组织:为了表示迷宫,设置一个二维数组(migong) mg[ M][N] ,其中数组的每个元素表示一个方块的状态,0表示对应的方块为通道,1表示对应方块是墙。为了算法方便,在迷宫外面加一道墙,所以二维数组表示演变如下:(假设M=8,N=8)
mg[M+2][N+2]=
{
{1,1,1,1,1,1,1,1,1,1}, {1,0,0,1,0,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}
};
算法思路:
针对这类问题·,我们通常采用的是“”穷举法”,即从入口出发,顺某一方向向前试探,若能走通,则继续机组往前走,否则沿原路退回,换另一个方向在试探,直至把所有的路走完为止。为了保证在位置上都能沿原路退回(成为回溯),需要用一个后进先出栈来保存入口到当前位置的路径。为了保证试探的可走相邻方块不是已走路径上的方块,如(i,j)进栈,试探到(i+1,j),接着试探到(i,j),这样就是引起死循环,为此,在一个方块进栈后,将对应mg数组元素值改为-1,表示该方块为不可走相邻方块,当该方块退栈时,将其恢复为0
下面代码是给出找到一条路径的算法:
#include <iostream>
using namespace std;
#define MaxSize 100
#define M 8 //行数
#define N 8 //列数
int mg[M+2][N+2] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,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}
};
typedef struct
{
int row; //当前方块的行号
int column; //当前方块的列号
int direction ; //下一个可走的方格的方位编号 0:上 1:右 2:下 3:左
}Box; //定义方块类型
typedef struct
{
Box data[MaxSize] ;
int top ; //栈顶指针
}StackType ; //定义顺序栈类型
bool mgpath(int startRow,int startColumn,int endRow,int endColumn)
{
int i,j,k,direction,find;
StackType stackType ;
stackType.top = -1 ;
stackType.top++;
stackType.data[stackType.top].row = startRow ;
stackType.data[stackType.top].column=startColumn;
stackType.data[stackType.top].direction = -1 ;
mg[startRow][startColumn]= -1 ;
//栈不为空时循环
while(stackType.top > -1)
{
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column;
direction =stackType.data[stackType.top].direction;
//取栈顶方块
if(i==endRow && j==endColumn)
{
cout<<"迷宫路径如下"<<endl;
for(k=0;k<=stackType.top;k++)
{
cout<<"("<<stackType.data[k].row<<","<<stackType.data[k].column<<") " ;
if((k+1)%5 == 0)
cout<<endl;
}
cout<<endl;
return true ;
}
find = 0 ;
//找下一个可走的方块
while(direction < 4 && find==0)
{
direction++;
switch(direction)
{
case 0 :
i=stackType.data[stackType.top].row-1;
j=stackType.data[stackType.top].column;
break;
case 1 :
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column+1;
break;
case 2:
i=stackType.data[stackType.top].row+1;
j=stackType.data[stackType.top].column;
break;
case 3:
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column-1;
break;
}
if(mg[i][j] == 0)
find = 1 ; //找到下一个可走的相邻方块
}
//找到下一个可走方块
if(find == 1)
{
stackType.data[stackType.top].direction=direction ;
stackType.top++;
stackType.data[stackType.top].row=i ;
stackType.data[stackType.top].column=j ;
stackType.data[stackType.top].direction = -1 ;
mg[i][j]=-1;
}
else
{
//没有路径可走,则退栈
mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ;
stackType.top--;
}
}
return false;
}
int main()
{
if(!mgpath(1,1,M,N))
cout<<"该迷宫问题没有解!"<<endl;
cout<<"结束"<<endl;
return 0 ;
}
运行结果如下:
拓展:输出所有能从入口到出口的的路径和其中最短路径
代码如下:
#include <iostream>
using namespace std;
#define MaxSize 100
#define M 8 //行数
#define N 8 //列数
int mg[M+2][N+2] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,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}
};
typedef struct
{
int row; //当前方块的行号
int column; //当前方块的列号
int direction ; //下一个可走的方格的方位位编号 0:上 1:右 2:下 3:左
}Box; //定义方块类型
typedef struct
{
Box data[MaxSize] ;
int top ; //栈顶指针
}StackType ; //定义顺序栈类型
void mgpath(int startRow,int startColumn,int endRow,int endColumn)
{
int i,j,k,direction,find;
int count=1; //路径数计数
int minlen=MaxSize; //最短路径长度
StackType path;
StackType stackType ;
stackType.top = -1 ;
stackType.top++;
stackType.data[stackType.top].row = startRow ;
stackType.data[stackType.top].column=startColumn;
stackType.data[stackType.top].direction = -1 ;
mg[startRow][startColumn]= -1 ;
//栈不为空时循环
while(stackType.top > -1)
{
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column;
direction =stackType.data[stackType.top].direction;
//取栈顶方块
if(i==endRow && j==endColumn)
{
count++; //路径树基数
cout<<"迷宫路径如下"<<endl;
for(k=0;k<=stackType.top;k++)
{
cout<<"("<<stackType.data[k].row<<","<<stackType.data[k].column<<") " ;
if((k+1)%5 == 0)
cout<<endl;
}
cout<<endl<<endl;
//因为空栈top=-1,当栈里面有元素是为top=0,1,2,3,4...MaxSize-1 ,总共有MaxSize个元素
//所以stackType.top+1为栈里面的元素个数
if(stackType.top+1<minlen)
{
//比较输出最短路径
for(k=0;k<=stackType.top;k++)
{
path.data[k]= stackType.data[k];
}
minlen=stackType.top+1;
}
//让该位置变为其他路径的可走结点
mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ;
stackType.top--;
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column;
direction= stackType.data[stackType.top].direction;
}
find = 0 ;
//找下一个可走的方块
while(direction < 4 && find==0)
{
direction++;
switch(direction)
{
case 0 :
i=stackType.data[stackType.top].row-1;
j=stackType.data[stackType.top].column;
break;
case 1 :
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column+1;
break;
case 2:
i=stackType.data[stackType.top].row+1;
j=stackType.data[stackType.top].column;
break;
case 3:
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column-1;
break;
}
if(mg[i][j] == 0)
find = 1 ; //找到下一个可走的相邻方块
}
//找到下一个可走方块
if(find == 1)
{
stackType.data[stackType.top].direction=direction ;
stackType.top++;
stackType.data[stackType.top].row=i ;
stackType.data[stackType.top].column=j ;
stackType.data[stackType.top].direction = -1 ;
mg[i][j]=-1;
}
else
{
//没有路径可走,则退栈
mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ;
stackType.top--;
}
}
cout<<"最短路径如下:"<<endl;
cout<<"长度:"<<minlen<<endl;
cout<<"路径:"<<endl;
for(k=0;k<minlen;k++){
cout<<"("<<path.data[k].row<<","<<path.data[k].column<<") " ;
if((k+1)%5==0) //输出时每5个结点换一行
cout<<endl;
}
cout<<endl;
}
int main()
{
mgpath(1,1,M,N);
cout<<"结束"<<endl;
return 0 ;
}
程序运行结果:
........................................................................................
.......................................................................................
.......................................................................................
读者可能会奇怪,为什么就能确定遍历完所有路径?
大家仔细想想,我每个方块按照(不包括边框的方块墙,我们自己加上去的)按照上、右、下、左的方向进行遍历(代码中direction代表方向),也就是说,所走的方块上下左右我都遍历完了。假设我们可以走的方块数目为W个,我们实际上遍历了4的W次方条可能路径,在进行剪枝操作,选取可行的路径。
第一个可行方块的遍历顺序为,上,右,下,左。如果第一个可行方块的上边有可行方块,我们将选取1第一方块的上面方块做为第二可行方块;如果第一方块上边没有可行方块,将找第一方块的右方块,如果是可行方块,将第一方块的有方块做为第二行方块,;如果第一方块的上,右没有可能方块,将遍历第一方块的下方块,如果还不是可行方块,将遍历第一方块的左方块,以此找到第二方块,第三方块.....
(此思路和递归雷同,相同的案例有:八皇后)