迷宫求解 低级版本(只能求一条路径)

前言:近日,室友的朋友因为一道题目来向他求助,由于本专业内能够熟练编程的人很少,所以自然而然的接到了本人的手上,不看不要紧,一看“吓一跳”,这道题目是典型的迷宫问题,然而本人其实只是了解并没有系统性地学习过迷宫算法,故自研了一套能够应付大多数迷宫问题的代码方案。解完后才被告知该题系蓝桥杯某次比赛的真题……(有点汗流浃背了)

题目如下:

 一、题目分析

        所谓迷宫,就是有墙有路的一块封闭地域,路呢有通路,也有死路。用数学的方法去描述迷宫,一般都是矩阵,因为它的相性非常好(特别是可视化方面),也有很多类似的地方。那么在c语言当中,描述矩阵的方法肯定就是二维数组了,所以我们的迷宫将以二维数组的形式出现在我们的程序中。

        作为迷宫,首先它有墙有路,一般情况下我们是用“0”“1”来表示墙和路的,在本道题目里,“0”代表了路,“1”则代表了墙;其次需要注意的是,迷宫是否拥有出口,这个是需要经过代码去判断的;再者则是走到死路怎么回到原来分支点(只有有分支才会走到死路,不然则是迷宫没有出口)。

       迷宫的存放我们已经解决了,而迷宫的出口判断其实也不难,没走出去就是没有出口;走到死路回到分支点,我们可以判断一个节点它是不是分支点,然后设置一个指针指向它,等走到死路的时候回到那个节点。这样一看,思路就非常清晰了,然而实现的过程总是状况百出的,以上方法在本人编程的时候均未想到,由于博主最近在上二叉树的遍历,递归算法是第一个冒出来的想法,故后面的程序采用了递归的思想。

        正在生成存放迷宫的数组mg [ ]……完成进度100%

        那么我们现在开始进入迷宫,从第一个格出发,留给我们的只有条路可以选,向右或者向下走;而对于一个基本的方位来讲应该是四个方向,上下左右均能移动,因此我们需要判断我们的下一个方向是否能走,判断的方法则是对我们下一个要走的地点进行比较。假设我们从起点出发,那我们当前所处的位置为(0,0),我们想要往右走,则需要确认(0,1)是否为墙,也就是 mg[0][1]的值是否为1,是的话我们就不能向右边走,然后判断其他的方向,由于题目里的迷宫出口设定为左上角和右下角,所以向右向下走的判断可以放在前面以提高程序运算效率,向左向上的判断则放到后面去(因为你的趋势是往右下角方向走,向左向上都是趋势,除非路径需要,不然都不太可能往向去走,并且向走次数多的路径它一定不是迷宫的最短路径)。

       我们第一步判断完毕后要走到下一个位置,下一个位置我们通过递归的方法进入,递归的参数当前下一个位置的坐标,例如原起点mg[0][0],下一个位置是在右边,那么我们给递归函数F(x)的值是mg[0][1]的坐标(0和1)。这里我们给递归函数命名为zmg找迷宫),则它需要两个数,行和列即zmg(int x,int y)。递归函数里的代码就分为了大部分,第一个,墙的判断;第二个,进行递归;第三个,输出行走路径(应题目要求)。前面两个我们已经解决了,现在我们来探讨第三个部分。

       输出行走的路径,那么我们就要想到走到“死路”了怎么办。对于递归算法本身来讲,就是要穷尽每一种可能性,因此在找路径的方法上,使用递归可以完全无视掉“死胡同”对查找的影响,但是输出就不一样了。根据题目的输出要求,我们只能输出正确的路径,怎么样去控制“死路”的输出是一个问题。从逻辑上看,“死路”之所以为“死路”,是因为它走不下去了,这个时候它应该满足三个方位上都是墙,还有一个方位是它的入口,那就非常的明显了,我们需要对迷宫的每个位置进行标记查重,一旦有出现像这样三面是墙,还有一路是已经走过的情况,我们就可以判定是走到“死路”了,对于“死路”我们将不进行任何的操作因为递归算法会自动结算并返回到分支点,这个是必定的,对于一个有出口的迷宫来讲,有分支点你才有机会进入“死路”。我们可以直接对已经走到的位置在原本的mg数组里赋值为1,这本轮内容和对下一步的操作都没有影响,因为递归的永远是下一个操作位置

       对于正确的路径(这里指的是能达到出口的路径)我们不需要急着去输出,可以给他们做下标记存储一下,找到了终点后再统一输出,同时要设置表示找到了终点的变量,因为递归函数是共用一串代码的,走到“死路”时也会读一下你输出路径的代码,所以这里要区别开来,我们这里定义一个temp变量来表示出口有没有被找到,0代表没有被找到,1代表被找到了。标记数组的大小设置成和迷宫数组大小一致,用“1” “2” “3” “4”四个数字来代表四个不同方位,1代表“R”2代表“D”3代表“L”4代表“U”。判断是否找到出口后我们就可以输出了,输出的路径次数我们也需要用个变量来标记一下,这里定义一个nums变量,当本次递归满足1.正确的路径2.找到出口时则输出,输出的方位则根据之前的标记来选择。然而在实际输出中仅仅是这样程序还是会出错,为什么呢?问题还是出现在分支点上,一个分支点有多条路径方向的选择,在程序代码里上下左右这四个方位顺序不可能同时进行判断,根据最优路径原则我们是把向右和向下的方位提前了,并且在这个顺序下有一个方位路径找到了出口,原本的路径是要输出的,但是本轮递归程序并没有结束,继续往下读,把分支上其他的点也给输出了,这样程序就不对了。我们可以设置一个变量re,当有一条路径输出时(re=1),分支点的其他路径就不再输出,这样就保证了完整的一条路径能够输出,但也因此只可以输出一条路径,在面对多解迷宫时还需要添加额外的代码来输出别的路径。

二、代码实现

#include<bits/stdc++.h>
#include<string>
using namespace std;
#define N 100
int mg[N][N];//迷宫地形数组,0代表通道,1代表围墙 
int road[N*N];//路径数组,存储每个经过路径点位的下一步操作 
int js[N][N];//操作寄存数组,存储每个经过路径点位的操作类型
int i,j,n,m,temp,nums=0;
void zmg(int row,int line){//找迷宫的函数,需要知道当前位置的x,y,参照数组mg、js以及用于判断的行和列 
	 int r=row,l=line,re=0;
	 if(mg[r][l+1]==0&&l<n-1&&re==0){//向右走满足1.右边的格子没有走过2.不越界3.其他方位还没有找到出口 
	 	mg[r][l]=1;//目前的点位标记为墙,代表已经走过 
	 	js[r][l]=1;//标记路径的下一个操作(上下左右) 
	 	zmg(r,l+1);//递归 
	 	if(temp==1)re++;//该路径已经找到出口,其他路径不得输出 
	 }
	if(mg[r+1][l]==0&&r<m-1&&re==0){//向下走 
	 	mg[r][l]=1;
	 	js[r][l]=2;
	 	zmg(r+1,l);
	 	if(temp==1)re++;
	 }
	if(mg[r][l-1]==0&&l>1&&re==0){//向左走 
	 	mg[r][l]=1;
	 	js[r][l]=3;
	 	zmg(r,l-1);
	 	if(temp==1)re++;
	 }
	if(mg[r-1][l]==0&&r>1&&re==0){//向上走 
	 	mg[r][l]=1;
	 	js[r][l]=4;
	 	zmg(r-1,l);
	 	if(temp==1)re++;
	 }
	if(r==m-1&&l==n-1)//目前位置达到出口 
	   temp=1;//标记为找到出口 
	if(temp){//找到出口了,在出口路径上的位置输出方位操作 
	   road[nums++]=js[r][l];//存储方位操作和操作次数,nums为操作次数 
	   if(r==0&&l==0)return;//递归回入口,退出递归程序 
    }
	
}
int main(){
	printf("请输入设定的列数:");
	scanf("%d",&n);
	printf("请输入设定的行数:");
	scanf("%d",&m);
//这两步其实也是在设定终点的坐标
	printf("请输入迷宫,0代表通道,1代表围墙:\n");
	for(i=0;i<m;i++)//给迷宫生成地形 
	for(j=0;j<n;j++)
	scanf("%d",&mg[i][j]);//注意scanf的输入格式,每输入完一个数后接上空格 
	zmg(0,0);	
    for(i=nums;i>0;i--){//对操作位输出,并倒串字符,因为出口是第一个完成递归的位置,入口是最后一个 
    	if(road[i]==1)printf("R");
    	if(road[i]==2)printf("D");
    	if(road[i]==3)printf("L");
    	if(road[i]==4)printf("U");
	}
	if(temp!=1)//temp不等于1即temp等于0,也就是没有找到终点 
    printf("Not Found404!");//没找到出口时输出 
	return 0;
}

运行结果

请输入设定的列数:7
请输入设定的行数:6
请输入迷宫,0代表通道,1代表围墙:
0 1 1 1 1 1 1
0 0 1 1 1 0 1
1 0 0 1 1 0 1
0 0 1 1 0 0 1
1 0 0 0 1 1 1
1 1 1 0 0 0 0
DRDDDRRDRRR

三、经验总结

       原本的代码里是用上了字符型数组去输出的,结果发生了一系列报错,也是涨了一波关于字符型数组、字符型指针的知识,好像用结构体就可以避免这样的情况发生,但本人不太喜欢用结构体去实现程序,也许需要稍微适应一下。

  • 37
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值