求解迷宫问题
问题描述:
有如图8×8的迷宫
OXXXXXXX
OOOOOXXX
XOXXOOOX
XOXXOXXO
XOXXXXXX
XOOOOXOO
XXXXXXXO
其中,O表示通路方块,X表示障碍方块。
假设入口位置为(0,0)出口为右下角方块位置(7,7)设计一个程序求指定入口到出口的一条迷宫路径。
分析
用n表示迷宫大小,用二维数组Maze存放迷宫,从(x,y)方块可以试探上、下、左、右4个方位。假设总是按从方位0到方位3的顺序试探,各方位对应的水平方向偏移量H[4]={0,1,0,-1}、垂直偏移量V[4]={-1,0,1,0}
- 思路一:深度优先遍历DFS
从(x,y)出发(初始为入口)搜索目标(出口)。对于当前方块(x,y),需要试探4个相邻的方块将对应的迷宫值由‘O’改为‘ ’(空格字符),当回过来时将其迷宫值恢复为‘O’。
代码1:
#include<stdio.h>
#define MAxN 10
int n=8;
char Maze[MAxN][MAxN]=
{
{'O','X','X','X','X','X','X','X'},
{'O','O','O','O','O','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','X','X','O','X','X','O'},
{'X','O','X','X','X','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','O','O','O','X','O','X'},
{'X','O','O','O','O','X','O','O'},
{'X','X','X','X','X','X','X','O'}
};
int H[4]={0,1,0,-1}; //水平偏移量,下标对应方位号1~3
int V[4]={-1,0,1,0}; //垂直偏移量
void disp(){ //输出一条迷宫路径
for(int i=0;i<n;i++){
printf(" ");
for(int j=0;j<n;j++)
printf("%c",Maze[i][j]);
printf("\n");
}
}
void DFS(int x,int y){ //求从(x,y)出发的一条迷宫路径
if(x==n-1&&y==n-1) //找到一条路径输出
{
Maze[n-1][n-1]=' ';
disp();
return;
}
else
{
for(int k=0;k<4;k++) //试探每一个方位
if(x>=0&&y>=0&&x<n&&y<n&&Maze[x][y]=='O')
{ //若(x,y)方块是可以走的
Maze[x][y]=' '; //将该方块迷宫值设置为空格字符
DFS(x+V[k],y+H[k]); //查找(x,y)周围的每一个相邻方块
Maze[x][y]='O'; //若从该相邻方块出发没有找到路径,恢复(x,y)迷宫值
}
}
}
int main(){
int x=0,y=0; //指定入口,出口默认(n-1,n-1)
printf("一条迷宫路径: \n");
DFS(x,y); //求(0,0)->(7,7)
}
- 思路二:广度优先遍历BFS
一定是最短路径
从(x,y)出发(初始为入口)搜索目标(出口)。由于在STL中queue不能顺序遍历,这里用一个数组作为非循环队列,front和rear分别为对头和队尾(初始值均设置为-1),每个进队元素有唯一下标
struct Position
{
int x,y; //当前方块位置
int pre; //前驱方块下标
};
定义队列:
Position qu[MAXQ];
int front=-1,rear=-1;
首先,将根入口方块(其pre置为-1)进队,队列不空时循环:出队方块p1作为当前方块(在队列数组中的下标为front),若p1为出口,通过队列数组qu反向退出迷宫路径并输出;
否则查找p1的每一个相邻方块p2,若p2位置有效(即p2.x>=0&&p2.y>=0&&p2.x<n&&p2.y<n)并且可走(Maze[p2.x][p2.y]=‘O’),置p2.pre=front(表示p2的前驱方块是p1)并将p2方块进队。
代码2:
#include<stdio.h>
#define MAXQ 100
#define MAxN 10
int n=8;
char Maze[MAxN][MAxN]=
{
{'O','X','X','X','X','X','X','X'},
{'O','O','O','O','O','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','X','X','O','X','X','O'},
{'X','O','X','X','X','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','O','O','O','X','O','X'},
{'X','O','O','O','O','X','O','O'},
{'X','X','X','X','X','X','X','O'}
};
int H[4]={0,1,0,-1}; //水平偏移量,下标对应方位号1~3
int V[4]={-1,0,1,0}; //垂直偏移量
struct Position //队列元素类型
{
int x,y; //当前方块位置
int pre; //前驱方块下标
};
Position qu[MAXQ]; //定义队列qu
int front=-1,rear=-1; //对头和队尾
void disp(int front){ //输出迷宫路径
int i,j;
for(i=0;i<n;i++)
for(j=0;j<n;j++) //将所有*改为O
if(Maze[i][j]=='*')
Maze[i][j]='O';
int k=front;
while(k!=-1)
{
Maze[qu[k].x][qu[k].y]=' '; //路径上的方块改为‘’
k=qu[k].pre;
}
for(i=0;i<n;i+=) //输出迷宫路径
{
printf(" ");
for(int j=0;j<n;j++)
printf("%c",Maze[i][j]);
printf("\n");
}
}
void BFS(int x,int y){ //求从(x,y)出发的一条迷宫路径
Position p,p1,p2;
p.x=x; //建立入口节点
p.y=y;
p.pre=-1;
Maze[p.x][p.y]='*'; //改为'*'避免重复查找
rear++; //入口方块入队
qu[rear]=p;
while(front!=rear){ //队不空时循环
front++;
p1=qu[front]; //出队方块p1
if(p1.x==n-1&&pi.y==n-1) //找到出口
{
disp(front); //输出路径
return;
}
for(int k=0;k<4;k++){
p2.x=p1.x+V[k]; //试探p1的每个相邻方位
p2.y=p1.y+H[k]; //找到p1的相邻方位p2
if(p2.x>=0&&p2.x<n&&p2.y>=0&&p2.y<n&&Maze[p2.x][p2.y]=='O')
{ //方块p2有效并且可走
Maze[p2.x][p2.y='*']; //改为'*'避免重复查找
p2.pre=front;
rear++;
qu[rear]=p2; //方块p2进队
}
}
}
}
int main(){
int x=0,y=0; //指定入口,出口默认(n-1,n-1)
printf("一条迷宫路径: \n");
DFS(x,y); //求(0,0)->(7,7)
}