DFS(深度搜索)——基于回溯法和递归

DFS

在学DFS之前,各位小盆友,最好能够搞明白什么是递归,下面我要考考你了哈。
下面是一道与递归有关的题目,看你要花多长时间AC吧,嘻嘻


一只小蜜蜂

有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。
这里写图片描述

Input

输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(0

Output

对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。 

Sample Input

2
1 2
3 6

Sample Output

1
3

细心的小伙伴可能发现了,这道题的解法跟之前我们所说的爬楼梯的解法是一样的,这是爬楼梯问题的链接http://blog.csdn.net/sand8o8time/article/details/76675769
在这道题目中,小蜜蜂只能向右走,也就是说小蜜蜂在数字上一步只能加一或者加2,跟爬楼梯的每次只能上一步,或上两步是一样的。这里我不再赘述

以下是AC代码:

#include
using namespace std;
long long floorNum(long long n)
{
	long long a = 1;
	long long b = 2;
	long long sum =0;
	if(n==0) return 0;
	if(n==1) return 1;
	if(n==2) return 2;
	for(int i=3;i<=n;i++)
	{
		sum = a+b;
		a = b;
		b = sum;
	}
	return sum;
}
int main()
{
	int t;
	cin>>t;
	while(t--){
		long long a,b;
		cin>>a>>b;
		cout<

从以上两道题我们可以看出,所谓递归,就是我们把一个很大的任务逐渐分割成较小的任务,再将较小的再进一步分割,最终将复杂的,庞大的任务分割成容易解决的较小的任务,最终达到解决整个任务的目的,比如就像斐波那契数列:1,1,2,3,5,8,13,21······,f(n),f(n+1),f(n+2)=f(n)+f(n+1),这样我们咋求第n个斐波那契数列时,就只需要从第n个斐波那契数往前递推,到n = 1和n = 2时,再一步一步回归,这就是斐波那契数的求法:

long long Fib(int n){
	if(n==1 || n==2) return 1;
	else return Fib(n-1)+Fib(n-2);
} 

至于回溯法,各位小盆友可以研究一下我之前写过的八皇后问题(很经典的一个问题哦)http://blog.csdn.net/sand8o8time/article/details/77057016

如果想看八皇后问题的棋盘模拟,推荐大家看一下http://blog.csdn.net/justme0/article/details/7540425


OK,明白了递归和回溯,那么我们就能研究DFS的问题了(深度搜索)

**顾名思义:**它的思想是从一个点开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先的概念。
下面给出一个连接,很好的解释了如何实现深度搜索http://blog.csdn.net/liangzhaoyang1/article/details/51415719

下面通过一道题目给大家讲解一下DFS的具体步骤:

POJ 3984

迷宫问题

Description:

定义一个二维数组:

int maze[5][5] = {

0, 1, 0, 0, 0,

0, 1, 0, 1, 0,

0, 0, 0, 0, 0,

0, 1, 1, 1, 0,

0, 0, 0, 1, 0,

};

它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

Input:

一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

Output:

左上角到右下角的最短路径,格式如样例所示。

Sample Input:

0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

Sample Output:

(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)

**题目分析:**这道题目就是一个很明显的DFS题,这道题的搜索路径为:(0, 0)—>(1, 0)—>(2, 0)—>(3, 0)—>(4, 0)—>(3, 0)—>(2, 0)—>(2, 1)—>(2, 2)—>(2, 3)—>(2, 4)—>(3, 4)—>(4, 4),这样找到了路径一定是最短的,有的同学会说,在走到(2, 2)时难道就不会往(1, 2)走吗?其实我也在想这个问题,最后发现这个点每正确走一步能够通往终点的路径时都会把当前行列都走完,那么走的每一步直到走到死胡同你才会一步一步返回到上一个分叉路口,那么如果有一条路存在环的情况,如(2, 2)—>(1, 2)—>(0, 2)—>(0, 3)—>(0, 4)—>(1, 4)—>(2, 4),点从(2, 2)过来的时候,就一定会沿着这一行走完,把走过的路给标记成已经走过,这时我能够走到(2, 4)这个位置从这到达终点,而不是绕一圈到(2, 4)再到达终点(若能从(2, 4)走到终点);

下面是AC代码:

#include
#include
int n,m,i,j,k,flag,count;
int data[5][5],vis[5][5];//data用来存储迷宫,vis用来判断该点的是否被走过 
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//用来控制上下左右行走 
struct Path
{
	int a,b;
}path[10][10];//用来保存行走的路线

void dfs(int x,int y)
{
	int i,xx,yy;
	if(x==4 && y==4){//如果找到了终点,把flag赋值为1,在回归时,一步一步直接跳出dfs函数 
		flag = 1;
		return;
	}
	for(i=0;i<4;i++)
	{
		xx = x+dir[i][0];//一共四个方向,所以for循环的i判断到i<4,分别可以上下左右移动; 
		yy = y+dir[i][1];
		if(vis[xx][yy]==0 && data[xx][yy]==0 && xx>=0 && xx<5 && yy>=0 && yy<5)//如果该点没被走过,而且该点不是墙,而且没有越界 
		{
			vis[xx][yy] = 1 ;//把该点标记为已经走过 
			path[xx][yy].a = x ;//将该点的坐标存在path中,方便之后的对路径的输出 
			path[xx][yy].b = y ;
			dfs(xx,yy);//在该点的基础上继续走,如果这条路走得通,那么一步步返回的时候flag就已经到终点变成了1,如果走不通,那么flag依旧是0 

			if(flag) return;//之前的路如果走得通的话,这里就能直接跳出dfs函数

			path[xx][yy].a = -1;//如果不能,那么在回归的时候将之前走过的标记过的路径再还原回去  
			path[xx][yy].b = -1;
			vis[xx][yy] = 0;
		}
  	}
}

int main()
{
	for(i=0;i<5;i++)//输入迷宫
		for(j=0;j<5;j++)
			scanf("%d",&data[i][j]);
	memset(vis,0,sizeof(vis));//初始化vis,vis用来标记走过的点 
	memset(path,-1,sizeof(path));// 初始化path,path用来记录走过的路径 
	flag = 0;//用来判断这条路是否走得通 
	dfs(0,0);//从起点(0,0)开始 
	for(i = 0;i<5;i++)//将路径输出 
    	for(j = 0;j<5;j++)
			if(path[i][j].a!=-1)
			printf("(%d, %d)\n",path[i][j].a,path[i][j].b);
	printf("(4, 4)\n");//打印终点坐标 
	return 0;
} 

喜欢的小伙伴给我顶一下哦(_)/~~

你可能感兴趣的:(算法理论)