深搜和广搜 迷宫最短路径

一、概述

初次接触迷宫广搜问题,整了几天才渐渐明白其中原理;附上自己的学习经验(适合初学者);

二、问题

输入:第一行输入一个整数,表示有几组数据; 第二行输入两个整数 行和列 (r ,c);接下来输入r行c列的矩阵(S是入口,E是出口,#代表墙,. 是可行区域)

输出:从入口S到E的最短路径 (假设输入数据必定有一条可行路径)

例如:输入:

2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
5 9
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########

输出:

5
9

三、深搜

1、基本思想:运用递归回溯遍历整个迷宫,计算出所有可行路径,再比较出最短的;

2、代码:

#include<stdio.h>
#include<string.h>

char a[100][100];
int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; //定义出在当前位置走下一步的四种情况
int r,c,s1,s2,minn,n;
void dfs(int x,int y,int count)
{
	
	for(int i=0;i<4;i++)     //每次位置的四种可能走的四周 都遍历一遍
	{
		int tx=x+dx[i];      //尽量在函数内定义tx,ty 避免全局变量在递归时的影响
		int ty=y+dy[i];
		if(tx>=0&&ty>=0&&tx<r&&ty<c&&(a[tx][ty]=='.'||a[tx][ty]=='E'))  // 前四个判断是否越界,后面判断避免墙(#)
		{
			if(a[tx][ty]=='E')   //先判断是否已经结束
				{
					if(count<minn) minn=count;   
					n=1;
				}
			else 
			{
			a[tx][ty]='#';                   //把走过的地方标记为墙,避免后面的搜索再走
		/*	for(int i=0;i<r;i++)
			{
				printf("\n");
				for(int j=0;j<c;j++)
			    printf("%c",a[i][j]);
			}
			printf("\n");*/  //输出每步走的地图,这个很实用,观察每一次行走的记录地图,找出欠缺的部分
			dfs(tx,ty,count+1);     //回溯,把不符合的尝试路径清除,同时把墙(#)变为(.)
			a[tx][ty]='.';
			}
		}
	}
}
int main()
{
	int m,i,j;
	scanf("%d",&m);
	while(m--)
	{
		memset(a,0,sizeof(a));
		minn=100000;n=0;
		scanf("%d%d",&r,&c); //输入 行 列 
		for(i=0;i<r;i++)
		{
			getchar();       //消除每一行输入后的回车,这个很关键
			for(j=0;j<c;j++)
			{
				scanf("%c",&a[i][j]);
				if(a[i][j]=='S')
					{s1=i;s2=j;}   //记录开始的坐标
			}
		}
		dfs(s1,s2,0);  //深搜调用,从S开始,count初始为0
		if(!n)
			printf("impossible\n");
		else
			printf("%d\n",minn+2);  // +2是为了把起始S和结束E都算在总步数中
	}
	return 0;
 }

四、广搜

1、基本思想:运用队列先进先出的特性可以更好的处理下次要走的方向; 广搜可以同时走三个方向,弹出一个方向就再存入弹出的方向能走的三个方向;如此第一次找到符合条件的就是最短路径;

2、代码(基本类似于深搜):

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

typedef pair<int ,int> v;     //这个是c++ pair运用, typedef自定义了一个pair类型的 v;(内部的两个类型可以任意定义,这里定义为两个整型)
queue<v> q;    //相当于定义 类型为 pair的队列q

int r,c,count,s1,s2,dx[]={-1,0,1,0},dy[]={0,-1,0,1}; 
char a[100][100]; int b[100][100];
int bfs()
{
	q.push(v(s1,s2));   //存入开始的位置
	while(!q.empty())   //判断是否为空,没有满足的可走方向,就不会压入队列元素,就会结束循环
	{
		v p=q.front();   //定义v类型的p 来存储队列q的第一个元素,然后弹出,循环遍历判断存入,弹出位置可走的四个方向
		q.pop();
		for(int i=0;i<4;i++)
		{
			int tx=p.first+dx[i];
			int ty=p.second+dy[i];
			if(tx>=0&&ty>=0&&tx<r&&ty<c&&a[tx][ty]!='#'&&!b[tx][ty])
			{
				b[tx][ty]=b[p.first][p.second]+1;
			//	a[tx][ty]='#';   标记没有作用 
				q.push(v(tx,ty));
				if(a[tx][ty]=='E') return b[tx][ty];   //此处的判断应在b[tx][ty]的赋值之后,不然会导致输出结果为0的错误
			}
		}
	}
	
}

int main()
{
	int m,i,j;
	scanf("%d",&m);
	while(m--)
	{
		memset(a,0,sizeof(a));  count=0;
		memset(b,0,sizeof(b));
		while(!q.empty()) q.pop();   // 清空队列,在多次循环中有着很大作用
		scanf("%d%d",&r,&c); //输入 行 列 
		for(i=0;i<r;i++)
		{
			getchar();
			for(j=0;j<c;j++)
			{
				scanf("%c",&a[i][j]);
				if(a[i][j]=='S')
					{s1=i;s2=j;}
			}
		}
		b[s1][s2]=1;
		printf("%d\n",bfs());
	}
	return 0;
 } 


五、总结

在最短路径方面,广搜有着很大的优势,可以节省很多时间,毕竟不用像深搜一样遍历所有结果才能得到最短的;而且在一些迷宫搜索题目中给出的测试数据一般深搜是很难通过的;推荐使用广搜来寻找最短路径; 这才是搜索的入门阶段,希望可以继续探索更加有趣的难题,有自己的目标才是努力下去的动力,继续努力!

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值