【基础算法】深度优先搜索DFS与广度优先搜索BFS

【基本算法】深度优先搜索DFS与广度优先搜索BFS

深搜(depth first search)和广搜(breadth first search)是两种基本搜索算法,均采用穷举策略
下面以老鼠走迷宫(maze.cpp)为例给出它们的模板

题目

【问题描述】
一只老鼠从迷宫的左上角走到右下角(如下图),中间不能穿越障碍(阴影部分)
一种走法
任务:给出迷宫的形状,请你求出老鼠经过的最少格子数量。

【输入格式】
第一行,为两个整数n和m(1≤n,m≤20),表示迷宫总共有几行几列;
从第二行开始到第n+1行,每行m列为迷宫形状,“0”表示无障碍,“1”表示有障碍,
相邻连个数中间用一个空格隔开。

【输出格式】
输出一个整数,表示经过的最短路径长度(每个格子长度为1);
如果没有一条可行的路,则输出“-1” 。

【输入样例】maze.in
6 9
0 0 0 1 1 0 0 0 1
0 1 0 0 0 0 1 0 1
0 1 0 1 0 1 1 0 1
0 1 0 1 0 0 0 0 0
0 1 1 0 1 1 0 1 0
0 0 0 0 0 0 0 1 0

【输出样例】maze.out
14

深度优先搜索DFS

深搜的思想是从一个起点开始,单向向前进,遇到不合法的情况就回溯。

  • 深搜需要运用到递推与回溯
  • (回溯法是思想,深搜是算法,策略!)
  • 基本模型
void DFS( 状态i )
{
	操作; 
	
	跳到状态i+1;
	if( 状态i+1合法 )
		DFS( 状态i+1 );
	
	操作; 
}

按这个模型,得到maze.cpp的深搜法代码:

//DFS
#include<bits/stdc++.h>
using namespace std;

int n,m,x,y;
bool Map[21][21],Way[21][21];//Map记录障碍,Way记录是否走过 
const int fx[4]={-1,0,1,0};//老鼠向四个方向移动,点的变化量(上,右,下,左) 
const int fy[4]={0,1,0,-1};
int len=0x7f7f7f;//len是进过距离的最小值 

void DFS(int step,int a,int b)     //准备进行第step步,当前位置a,b 
{
	int i,x,y;   //attention!!y1是函数!!会歧义!!!! 
	for(i=0;i<4;i++){ //枚举四个方向 
		//新位置 
		x=a+fx[i];
		y=b+fy[i];
		
		//判断新位置是否合法,不合法就不能走,跳到下一个for循环 
		if(x>0&&x<=n&&y>0&&y<=m&&!Map[x][y]&&!Way[x][y]){
			
			Way[x][y]=1;//做走过的标记 
			
			if(x==n&&y==m)len=min(len,step);//到终点了,记录最小步数
			
			DFS(step+1,x,y);//准备执行第step+1步,当前位置x,y
			
			Way[x][y]=0;//取消走过的标记:其他走法可能还要用 
		}
	}
}

int main(){
	freopen("01 maze.in","r",stdin);
	freopen("01 maze.out","w",stdout);
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		 	scanf("%d",&Map[i][j]);
	
	Way[1][1]=1;//出发位置标记 
	
	DFS(2,1,1);//开始
	 
	if(len==0x7f7f7f)puts("-1");
	else printf("%d",len);
	
	return 0;
}

当然,既然我们只求最短路径长度,可以对深搜过程进行优化:

//判断新位置是否合法,不合法就不能走,跳到下一个for循环 
		if(x>0&&x<=n&&y>0&&y<=m&&!Map[x][y]&&!Way[x][y]){
			
			Way[x][y]=1;//做走过的标记 
			
			if(x==n&&y==m)len=min(len,step);//到终点了,记录最小步数
			else 
			{
				if(step+1<len)//优化:如果步数已经超过目前的最小步数了,就不再执行 
					DFS(step+1,x,y);//准备执行第step+1步,当前位置x,y
			}
			
			Way[x][y]=0;//取消走过的标记:其他走法可能还要用 
		}

广度优先搜索BFS

广搜的思想是从起点开始搜索每一个点,它与深搜的不同在于它同时考虑所有的情况(所以叫广度)

  • 利用队列实现广搜
  • 基本模型
void BFS(){
	while(存在下一种状态OR队列不空){
		得到下一种状态;//如果每种状态存在多组数据,可以用结构体存储 
		操作; 
		此状态入队;
		if(状态合法)操作; 
	} 
}

队列:

1 2 3(1得到) 4(1得到) 5(2得到) 6(2得到) 7(3得到) ……
状态1 状态2 状态3 状态4 状态5 状态6 状态7 ……

按这个模型,得到maze.cpp的广搜法代码:

#include<bits/stdc++.h>
using namespace std;

int n,m,head,tail;//head,tail分别为队列的头指针与尾指针--也就是下标 
bool Map[105][105],Way[105][105];
const int fx[4]={-1,0,1,0};
const int fy[4]={0,1,0,-1};
int x,y,len;

struct st
{
	int x;
	int y;
	int len;
};
st loc[10005];//定义为队列数组--队列只是模型 

void BFS(){
	while(head<tail)
	{
		head++;
		for(int i=0;i<4;i++)
		{
			int a=loc[head].x+fx[i];
			int b=loc[head].y+fy[i];
			if(a>0&&a<=n&&b>0&&b<=m&&!Way[a][b]&&!Map[a][b])//判断合法 
			{
				tail++;
				loc[tail].x=a;
				loc[tail].y=b;
				loc[tail].len=loc[head].len+1;
				Way[a][b]=1;
				if(a==n&&b==m)
				{
					printf("%d",loc[tail].len);
					return;//能达到最小值就结束了 
				}
			}
		}
	}
	puts("-1");//程序自然结束,没有到过终点 
	return;
}

int main()
{
	freopen("01 maze.in","r",stdin);
	freopen("01 maze.out","w",stdout);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&Map[i][j]); 
	
	//初始化
	memset(loc,0,sizeof(loc));
	head=0;
	tail=1;
	Way[1][1]=1;
	loc[tail].x=1;
	loc[tail].y=1;
	loc[tail].len=1;
	
	BFS();
	
	return 0;
}
发布了26 篇原创文章 · 获赞 3 · 访问量 918

猜你喜欢

转载自blog.csdn.net/Antimonysbguy/article/details/97899519