求解迷宫问题

 一、问题描述

给定一个MxN的迷宫图,求一条从指定入口道出口的迷宫路径,在行走中一步只能从当前方块移动到上、下、左、右相邻方块中的一个方块。

一般情况下,所求的迷宫路径为简单路径,即在求得的路径上不会重复出现同一个方块。一个迷宫图的迷宫路径可能有多条,这些路径有长有短,这里仅考虑用栈求一条从指定入口到出口的迷宫路径。

 二、数据组织

为了表示迷宫,设置一个数组mg,其中每个元素表示一个方块的状态,为0时表示对应方块是通道,为1时表示对应方块是障碍物。为了算法方便,一般在迷宫外围加一条围墙。

另外,在算法中用到的栈采用顺序栈结构储存,即将迷宫栈声明如下:

typedef struct {
	int i;  //当前方块的行号
	int j;  //当前方块的列号
	int di; //di是走到下一相邻可走方块的方位号
}Box;       //方块类型

typedef struct {
	Box data[MaxSize];
	int top;  //栈顶指针
}StType;			  //顺序栈类型

三、设计运算算法

对于迷宫中的每个方块,有上、下、左、右4个方块相邻,第i行第j列的当前方块的位置记为(i,j),规定上方方块为方位0,并按顺时针方向递增编号。在试探过程中,假设按从方位0到方位3的方向查找下一个可走的相邻方块。

求迷宫问题就是在一个指定的迷宫中求出从入口到出口的一条路径。在求解时采用“穷举法”,即从入口出发,按方位0到方位3的次序试探相邻的方块,一旦找到一个可走的相邻方块就继续走下去,并记下所走的方位;若某个方块没有相邻的可走方块,则沿原路退回到前一个方块,换下一个方位再继续试探,直到所有可能的通路都试探完为止。

为了保证在任何位置上都能沿原路退回(称为回溯),需要保存从入口到当前位置的路径上走过的方块,由于回溯的过程是从当前位置退到前一个方块,体现出后进先出的特点,所以采用栈保存走过的方块。

若一个非出口方块(i,j)是可走的,将它进栈,每个刚进栈的方块,其方位di置为-1(表示尚未试探它的周围),然后开始从方位0到方位3试探这个栈顶方块的四周,如果找到某个方位d的相邻方块(i1,j1)是可走的,则将栈顶方块(i ,j)的方位di置为d ,同时将方块(i1,j1)进栈,再继续从方块(i1,j1)做相同的操作。若方块(i ,j)的四周没有一个方位是可走的,将它退栈,前一个方块( x,y)变成栈顶方块,再从方块(x,y)的下一个方位继续试探。

在算法中应保证试探的相邻走方块不是已走路径上的方块。如方块(i,j)已进栈,在试探方块(i+1,j)的相邻可走方块时又会试探到方块(i,j)。也就是说,从方块(i,j)出发会试探方块(i+1,j),而从方块(i+1,j)出发又会试探方块(i,j),这样可能会引起死循环,为此在一个方块进栈后将对应的mg数组元素值改为-1(变为不可走方块),当退栈时(表示该栈顶方块没有相邻可走方块)将其恢复为0。

求解迷宫中从入口(xi,yi)到出口(xe,ye)的一条迷宫路径的过程如下:

将入口(xi,yi)进栈(其初始方位设置为-1);
mg[xi][yi] = -1;
while(栈不空){
    取栈顶方块(i,j,di);
    if((i,j)是出口(xe,ye))
    {    输出栈中的全部方块构成一条迷宫路径;
         return true;
    }
    查找(i,j,di)的下一个相邻可走方块;
    if(找到一个相邻可走方块)
    {
        该方块的位置为(i1,j1),对应方位为d;
        将栈顶方块的di设置为d;
        (i1,j1,-1)进栈;
        mg[i1][j1] = -1;
    }
    if(没有找到(i,j,di)的任何相邻可走方块)
    {
        将(i,j,di)出栈;
        mg[i][j] = 0;
    }
}
return false;

根据上述过程得到求迷宫问题的算法如下:

bool mgpath(int xi, int yi, int xe, int ye) {		//求解路径为(xi,yi)->(xe,ye)
	Box path[MaxSize], e; 
	int i, j, di, i1, j1, k;
	bool find;
	StType* st;		//定义栈st
	InitStack(st);		//初始化栈顶指针
	e.i = xi;		//设置e为入口
	e.j = yi;		
	e.di = -1;		
	push(st,e);		//方块e入栈
	mg[xi][yi] = -1;		//将入口的迷宫值置为-1,避免重复走到该方块
	while (!StackEmpty(st)) {		//栈不空时循环
		GetTop(st, e);		//取栈顶元素e
		i = e.i;
		j = e.j;
		di = e.di;
		if (i == xe && j == ye) {		//找到了出口,输出该路径
			printf("一条迷宫的路径如下:\n");
			k = 0;		//k表示路径中的方块数
			while (!StackEmpty(st)) {
				Pop(st, e);		//出栈方块e
				path[k++] = e;		//将e添加到path数组中
			}
			while (k>0){
				printf("\t(%d,%d)", path[k - 1].i, path[k - 1].j);
				if ((k + 1) % 5 == 0)		//每输出5个方块后换行
					printf("\n");
				k--;
			}
			printf("\n");
			DestroyStack(st);		//销毁栈
			return true;		//输出一条迷宫路径后返回true
		}
		find = false;
		while (di < 4 && !find) {		//找到方块(i,j)的下一个相邻方块(i1,j1)
			di++;
			switch (di){
			case 0:i1 = i - 1; j1 = j; break;
			case 1:i1 = i; j1 = j + 1; break;
			case 2:i1 = i + 1; j1 = j; break;
			case 3:i1 = i; j1 = j - 1; break;
			}
			if (mg[i1][j1] == 0)find = true;		//找到一个相邻可走方块,设置find为真
		}
		if (find) {		//找到一个相邻可走方块(i1,j1)
			st->data[st->top].di = di;		//修改原栈顶元素的di值
			e.i = i1;
			e.j = j1;
			e.di = -1;
			push(st, e);		//相邻可走方块e进栈
			mg[i1][j1] = -1;		//将(i1,j1)迷宫值置为-1,避免重复走到该方块
		}else {		//没有路径可走,则退栈
			Pop(st, e);		//将栈顶方块退栈
			mg[e.i][e.j] = 0;		//让退栈方块的位置变为其他路径的可走方块
		}
	}
	DestroyStack(st);		//销毁栈
	return false;		//表示没有路径可走,返回false
}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值