广度优先算法经典:迷宫最短路径问题(C语言实现)

今年上半年蓝桥杯考了类似的题(基本一样),本人算法菜鸡,考场上头铁用深搜写这个导致爆栈,最近闲下来研究了一下,写了一个找到最短出路的demo

问题描述

  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。(这个是蓝桥杯训练集的问题秒数,我真的佩服出题人的脑洞)

输入格式

  第一行两个整数n, m,为迷宫的长宽。
  接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。

(中间还有些不重要的我略过了...)

数据规模和约定

  有20%的数据满足:1<=n,m<=10
  有50%的数据满足:1<=n,m<=50
  有100%的数据满足:1<=n,m<=500

我的主要思路是用广度优先+队列去遍历,每走一步,后一个点会保存前一个点的指针,最后从终点进行回溯,一直找前一个点直到回到起点,当一个点已经被踩过后(也就是他的前一个点指针中存了值)就不能再被访问了,因为广度优先后来的路径到达同一个点的步数一定是大于或等于先前的路径到达同一个点的步数。

最后获得是一个链表,然后从终点递归回溯,打印出路径

下面上代码

先自己定义了个迷宫节点结构体和一个位置结构体,结构体主要存位置坐标,以及是否可以通过(0或1),以及来的路径(也就是上一个点)

struct Node{//迷宫节点
	int X;
	int Y;
	int value = 1;
	Node* before;
};


struct Cite{//位置队列
	int X;
	int Y;
}; 

Node a[502][502];
Cite quene[10000];

这里说一下,题目测试集数据规模定的500*500,为了避免C语言数组越界,我想到两种方法

1.分五种情况讨论,4条边界,和内部一整块

2.把给定迷宫围起来,直接写内部一整块的遍历方法就好

我定义成了502*502,等于加了个全是1的围栏,这样我就可以偷懒不用写4条边界的特殊情况了

然后是实现了一下环形队列的插入和删除方法

int head = 0;//队首
int tail = 0;//队尾

//入队 
void putInQuene(int a,int b){
	quene[tail].Y = a;
	quene[tail].X = b;
	tail++;
	tail = tail%10000;
	if(tail == head)printf("Quene is full!");
}

//出队 
Cite getOutQuene(){
	int re = head;
	head++;
	head = head%10000;
	return quene[re];
}

然后是回溯路径的函数,用了个递归

int findway(int y,int x){
	
	if(y!=1&&x!=1)
	findway(a[y][x].before->Y,a[y][x].before->X);

	if(a[y][x].before->X == x&&a[y][x].before->Y == y-1) printf("D");
	else if(a[y][x].before->X == x&&a[y][x].before->Y == y+1) printf("U");
	else if(a[y][x].before->Y == y&&a[y][x].before->X == x-1) printf("R");
    else if(a[y][x].before->Y == y&&a[y][x].before->X == x+1) printf("L");
    return 0;
}

最后是主函数

有一些题目要求如果步数相同的路径,就按照字母序输出最优先的路径,D(下) L(左) R(右) U(上) 分别代表四个走法,字母序的话我们不需要用路径进行字符串比较,只需调整添加进队列的顺序即可(也就是调整遍历顺序),以先下再左再右最后上的顺序加入队列,即达成了我们的目标

int main(){
	int x,y,i,j;
	scanf("%d %d",&y,&x);

	for(i=1;i<=y;i++){
		for(j=1;j<=x;j++){
			scanf("%d",&a[i][j].value);
		    a[i][j].before = NULL;
		    a[i][j].Y = i;
		    a[i][j].X = j;
		}
		
	}
	putInQuene(1,1);
	while(head != tail&&a[y][x].before == NULL){
		
		Cite temp = getOutQuene();
		i = temp.Y;
		j = temp.X;

        if(i==1&&j==1){
    		if(a[i+1][j].value == 0&&a[i+1][j].before == NULL)//下移 
			    {
			    	a[i+1][j].before = &a[i][j];
			    	putInQuene(i+1,j);
				}
			if(a[i][j-1].value == 0&&a[i][j-1].before == NULL)//左移 
			    {
			        a[i][j-1].before = &a[i][j]; 
					putInQuene(i,j-1);	
				}
			if(a[i][j+1].value == 0&&a[i][j+1].before == NULL)//右移 
			    {
			    	a[i][j+1].before = &a[i][j]; 
			    	putInQuene(i,j+1);
				}
			if(a[i-1][j].value == 0&&a[i-1][j].before == NULL)//上移 
			    {
			    	a[i-1][j].before = &a[i][j];
			    	putInQuene(i-1,j);
				}
		}
		
	    else if(a[i][j].before->X == j&&a[i][j].before->Y == i-1){//下移到达 (不可上移) 
		    
			if(a[i+1][j].value == 0&&a[i+1][j].before == NULL)//下移 
			    {
			    	a[i+1][j].before = &a[i][j];
			    	putInQuene(i+1,j);
				}
			if(a[i][j-1].value == 0&&a[i][j-1].before == NULL)//左移 
			    {
			        a[i][j-1].before = &a[i][j]; 
					putInQuene(i,j-1);	
				}
			if(a[i][j+1].value == 0&&a[i][j+1].before == NULL)//右移 
			    {
			    	a[i][j+1].before = &a[i][j]; 
			    	putInQuene(i,j+1);
				}
			
		}
		
    	else if(a[i][j].before->Y == i&&a[i][j].before->X == j+1){//左移到达 (不可右移) 	
	   
			if(a[i+1][j].value == 0&&a[i+1][j].before == NULL)//下移 
			    {
			    	a[i+1][j].before = &a[i][j];
			    	putInQuene(i+1,j);
				}
			if(a[i][j-1].value == 0&&a[i][j-1].before == NULL)//左移 
			    {
			        a[i][j-1].before = &a[i][j]; 
					putInQuene(i,j-1);	
				}
			if(a[i-1][j].value == 0&&a[i-1][j].before == NULL)//上移 
			    {
			    	a[i-1][j].before = &a[i][j];
			    	putInQuene(i-1,j);
				}	
    	}
    	
		else if(a[i][j].before->X == j&&a[i][j].before->Y == i+1){//上移到达 (不可下移) 
			
			if(a[i][j-1].value == 0&&a[i][j-1].before == NULL)//左移 
			    {
			        a[i][j-1].before = &a[i][j]; 
					putInQuene(i,j-1);	
				}
			if(a[i][j+1].value == 0&&a[i][j+1].before == NULL)//右移 
			    {
			    	a[i][j+1].before = &a[i][j]; 
			    	putInQuene(i,j+1);
				}
			if(a[i-1][j].value == 0&&a[i-1][j].before == NULL)//上移 
			    {
			    	a[i-1][j].before = &a[i][j];
			    	putInQuene(i-1,j);
				}
		}
		
	    else if(a[i][j].before->Y == i&&a[i][j].before->X == j-1){//右移到达 (不可左移) 	
	   
			if(a[i+1][j].value == 0&&a[i+1][j].before == NULL)//下移 
			    {
			    	a[i+1][j].before = &a[i][j];
			    	putInQuene(i+1,j);
				}
			if(a[i][j+1].value == 0&&a[i][j+1].before == NULL)//右移 
			    {
			    	a[i][j+1].before = &a[i][j]; 
			    	putInQuene(i,j+1);
				}
			if(a[i-1][j].value == 0&&a[i-1][j].before == NULL)//上移 
			    {
			    	a[i-1][j].before = &a[i][j];
			    	putInQuene(i-1,j);
				}	
    	}
    } 
    
    findway(y,x);

} 

然后上述代码加起来,加个头文件就是完成代码了,CSDN小透明希望对初学者有些许帮助,欢迎交流

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页