dfs经典例题(入门题)

  • Post author:
  • Post category:其他


再附上一篇DFS详解的,不明白DFS原理的同学可以看一看:

https://blog.csdn.net/li_jeremy/article/details/83714298

以下是全网收集整理的和自己写的部分,绝对保证dfs轻松入门。

核心代码:

关于dfs参数问题,什么在变化,就把什么设置成参数。

void dfs()//参数用来表示状态

{

if(到达终点状态)

{

…//根据题意添加

return;

}

if(越界或者是不合法状态)

return;

if(特殊状态)//剪枝

return ;

for(扩展方式)

{

if(扩展方式所达到状态合法)

{

修改操作;//根据题意来添加

标记;

dfs();

(还原标记);

//是否还原标记根据题意

//如果加上(还原标记)就是 回溯法

}

}

}


为什么有的时候得要标记,因为这可以防止打转现象的发生,你可以想象如果没有标记,你在迷宫围绕着某条路径打转


但有的时候需要标记却不需要还原标记,为何不需要还原标记,不还原的意义是防止下次递归时走到已走过的位置,还原是可   以让下次递归可以在经过原先递归走过的路径,当然不可能和原先一模一样的路径

DFS全排列

#include<iostream>
using namespace std;
int n;
int book[10] = {0};
int a[10];

void dfs(int index)
{
	if(index == n)  //递归终止条件
	{
		for(int i = 0;i < n;++i)
		    cout << a[i];
	    cout << endl;
	    return ;
	}
	
	else
	{
		for(int i = 1;i <= n;++i)
		{
			if(!book[i])
			{
				book[i] = 1;//标记位
				a[index] = i;//把符合的数字保存到数组中
				dfs(index+1);
				a[index] = 0;
				book[i] = 0;//还原标记,以便回溯
			}
		}
	}
}

void init()
{
    for(int i = 0;i < 9;++i)
	    book[i] = 0;	
}

int main()
{
	
	while(cin >> n)	
	{
		dfs(0);
		init();
	}
	  
	return 0;
} 


方格填数

如下的10个格子

+–+–+–+

|  |  |  |

+–+–+–+–+

|  |  |  |  |

+–+–+–+–+

|  |  |  |

+–+–+–+

(如果显示有问题,也可以参看【图1.jpg】)

填入0~9的数字。要求:连续的两个数字不能相邻。

(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

请填写表示方案数目的整数。

注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。


这就是道全排列的变式题(因为数字是0-9,且填入到格子中),因为它必须去检查所填的数字是否符合题目要求


思路:明显这是回溯题,可以用dfs做,当然你也可以套10个循环来做(TLE)


dfs的话这道题我们该如何进行递归,很明显这道题和全排列类似,所以我们可以一个方格一个方格来填空


由于题意要求连续的两个数字不能相邻


所以需要判断/

左,上,左上,右上

/,因为在你下面和右面的都还没填呢

#include<iostream>
#include<cmath>
#include<sstream>
#include<string>
#include<cstring>
#include<map>
#include<set>
#include<queue> 
#include<vector>
#include<algorithm>
using namespace std;

int ans = 0;
int dir[4][2] = {0,-1,-1,0,-1,-1,-1,1};
int book[10][10];
int a[10][10];
int vis[10];

/*左,上,左上,右上*/
bool check(int r,int c,int val)
{
    for(int k = 0;k < 4;++k)
	{
		int tr = r + dir[k][0];
		int tc = c + dir[k][1];
		if(tr < 1 || tc < 1 || tr > 3 || tc > 4)  continue;
		if(abs(a[tr][tc]-val) <= 1)  return false;
	}	
	
	return true;
}

void dfs(int row,int col)
{
	if(row == 3 && col == 4)
	{
		ans++;
		return ;
	}
	
	if(col == 5)
	{
		row += 1;
		col = 1;
	}
	
	
	
	for(int i = 0;i <= 9;++i)
    {
    	if(!book[row][col] && !vis[i] && check(row,col,i))
    	{
    		book[row][col] = 1;
    		vis[i] = 1;
    		a[row][col] = i;
    		dfs(row,col+1);
    		book[row][col] = 0;
    		vis[i] = 0;
    		a[row][col] = -2;
    	}
    	
    }
 
}



int main()
{
	for(int i = 0;i <= 5;++i)
	{
		for(int j = 0;j <= 9;++j)
		     a[i][j] = -2;
	}
	
	dfs(1,2);
	printf("%d\n",ans); 
	return 0;
}

寒假作业

现在小学的数学题目也不是那么好玩的。

看看这个寒假作业:

□ + □ = □

□ – □ = □

□ × □ = □

□ ÷ □ = □

(如果显示不出来,可以参见【图1.jpg】)

每个方块代表1~13中的某一个数字,但不能重复。

比如:

6  + 7 = 13

9  – 8 = 1

3  * 4 = 12

10 / 2 = 5

以及:

7  + 6 = 13

9  – 8 = 1

3  * 4 = 12

10 / 2 = 5

就算两种解法。(加法,乘法交换律后算不同的方案)

你一共找到了多少种方案?


同样是全排列变式题,


思路:一个一个填,递归的参数应该是下标,用一维数组来存储,一种做法是,把加减乘除比较,不符合则返回上一层,还有一种做法,把12个位置先填满,在判断是否符合条件,这样做,时间复杂度更高

#include<iostream>
#include<cmath>
#include<sstream>
#include<string>
#include<cstring>
#include<map>
#include<set>
#include<queue> 
#include<vector>
#include<algorithm>
using namespace std;

int ans = 0;

int vis[10];
int a[13];

void dfs(int index)
{
	if(index == 13)
	{
	    //为何不能a[10]/a[11] && a[10] % a[11] == 0,   11/5 == 2吧
		if(a[10] == a[11]*a[12])
		{	
			ans++;			
		}
		return ; 
	} 
	
	//+
	if(index == 3)
	{
		if(!vis[a[1]+a[2]])
		{
		    vis[i] = 1;
			a[index] = i;
			dfs(index+1);
			vis[i] = 0;
			a[index] = 0;
         }
		     return ;
	}
	
	//-
	if(index == 7)
	{
		if(a[4]-a[5] != a[6])
			return ;
	}
	
	//*
	if(index == 10)
	{
		if(a[7]*a[8] != a[9])
			return ;
	}
	
	
	
	
	for(int i = 1;i <= 13;++i)
	{
		if(!vis[i])
		{
			vis[i] = 1;
			a[index] = i;
			dfs(index+1);
			vis[i] = 0;
			a[index] = 0;
		}
	}
 
}



int main()
{
	dfs(1); 
	printf("%d\n",ans);
	return 0;
}

剪邮票

如【图1.jpg】, 有12张连在一起的12生肖的邮票。

现在你要从中剪下5张来,要求必须是连着的。

(仅仅连接一个角不算相连)

比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

请填写表示方案数目的整数。

注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。


思路:也是一道全排列变式题,只不过这里我们只截取其中5个格子判断是否连通,把选中的五个棋子的位置保存起来,如何判断连通呢?回溯即可



/*#include<iostream>
#include<cmath>
#include<sstream>
#include<string>
#include<cstring>
#include<map>
#include<set>
#include<queue> 
#include<vector>
#include<algorithm>
using namespace std;

int ans = 0;

int vis[9][9];
int dir[4][2] = {0,1,1,0,0,-1,-1,0};
int book[10000][5][5];


bool check(int count)
{
	if(count == 0)  return true;
	
	for(int k = 0;k < count;++k)
	{
		int flag = 1;
		for(int i = 1;i <= 3;++i)
        { 
	   	  for(int j = 1;j <= 4;++j)
	   	  {
   	  	        if(book[k][i][j] != vis[i][j])  flag = 0;
   	      }
       } 
       if(flag)
          return false;
	}
    
	return true;	  
}

void add(int a[][9],int count)
{
	    for(int i = 1;i <= 3;++i)
        { 
	   	  for(int j = 1;j <= 4;++j)
	   	  {
   	  	     book[count][i][j] = vis[i][j];
   	      }
       } 
}

void dfs(int r,int c,int sum)
{	
   if(sum == 4)
   {
   	   
   	   if(check(ans))
   	   {
		    add(vis,ans);
   	   	    ans++;
   	   	    
   	   	    for(int i = 1;i <= 3;++i)
            { 
		   	  for(int j = 1;j <= 4;++j)
		   	  {
	   	  	        printf("%d ",vis[i][j]);
	   	      }
	   	      printf("\n");
            } 
            
            printf("----------------------------------------------\n");
   	   }
   	       
   	   return ;
   }	
   
   for(int k = 0;k < 4;++k)
   {
   	    int tr = r + dir[k][0];
		int tc = c + dir[k][1];
		if(tr < 1 || tc < 1 || tr > 3 || tc > 4)  continue;
		if(!vis[tr][tc])
		{
			vis[tr][tc] = 1;
   	  	  	dfs(tr,tc,sum+1); 
   	  	  	vis[tr][tc] = 0;
		}
   }
 
} 



int main()
{
	for(int i = 1;i <= 3;++i)
    {
   	  for(int j = 1;j <= 4;++j)
   	  {
   	  	  if(!vis[i][j])
   	  	  {
   	  	  	  vis[i][j] = 1;
   	  	  	  dfs(i,j,0); 
   	  	  	  vis[i][j] = 0;
   	  	  	
   	  	  }
   	  }
    } 
	printf("%d\n",ans);
	return 0;
}*/




//别人的做法 
#include<iostream>
#include<cmath>
#include<sstream>
#include<string>
#include<cstring>
#include<map>
#include<set>
#include<queue> 
#include<vector>
#include<algorithm>
using namespace std;

int ans = 0;

int que[13][2];  //保存信息
int book[13][13];
int a[10];    
int vis[14];
int dir[4][2] = {0,1,1,0,0,-1,-1,0};
int flag = 1;




//通过回溯来判断 
int check(int x,int y)
{

	if(book[x][y] == 0) return 0;
	book[x][y] = 0;
		
	return 1+check(x-1,y)+check(x+1,y)+check(x,y-1)+check(x,y+1);	
}


void dfs(int sum,int last)
{	
   if(sum == 5)
   {
   	   
   	   memset(book,0,sizeof(book));
   	   //这里如何去判重
   	   
	   for(int i = 0;i < 5;++i)
	   {
	   	   book[que[a[i]][0]][que[a[i]][1]]= 1;
	   } 
	   
	   flag = 1;
	   if(check(que[last][0],que[last][1]) == 5)
	   {
	   	  ans++;
	   } 
	   
   	   return ;
   }	
   
   
   for(int i = last+1;i <= 12;++i)
   {
   	   if(!vis[i])
   	   {
   	   	  vis[i] = 1;
   	   	  a[sum] = i;
   	   	  dfs(sum+1,i);
   	   	  vis[i] = 0;
   	   	  a[sum] = -1;
   	   }
   }
   
} 



int main()
{
	int n = 1;
	for(int i = 1;i <= 3;++i)
    {
   	  for(int j = 1;j <= 4;++j)
   	  {
   	  	  //保存每个方格的横纵坐标 
   	  	  que[n][0] = i;
		  que[n++][1] = j; 
   	  }
    } 
	dfs(0,0);
	printf("%d\n",ans);
	return 0;
} 

初学dfs:先做最最最基础的题: http://codeup.cn/contest.php?cid=100000608。

做完了,你就入门了(绝对没有比这更基础的题了)

然后再做下面的题:

题型分类:

写过这些入门题后,我们可以将DFS题分为两大类:


1 . 地图型:这种题型将地图输入,要求完成一定的任务。因为地图的存在。使得题意清楚形象化,容易理清搜索思路。

POJ 1031棋盘问题

(类似八皇后问题)http://poj.org/problem?id=1321

AOJ 869-迷宫(遍历地图,四向搜索)

https://blog.csdn.net/chen_yuazzy/article/details/73656668


HDU 1035-Robot Motion(指定方向搜索,迷路(循环)判断)

http://acm.hdu.edu.cn/showproblem.php?pid=1035


HDU 1045-Fire Net(check函数,回溯)

http://acm.hdu.edu.cn/showproblem.php?pid=1045


HDU 1010-Tempter of the Bone(奇偶剪枝,回溯)

http://acm.hdu.edu.cn/showproblem.php?pid=1010

2 . 数据型:这种题型没有给定地图,一般是一串数字或字母,要求按照一定的任务解题。相对于地图型,这种题型较为抽象,需要在数据中进行搜索。数据以数组的形式存储,那么只要将数组也当作一张图来进行搜索就可以了。

HDU 1016-Prime Ring Problem(回溯、素数筛)

HDU 1258-Sum It Up(双重DFS递归,去重技巧)

HDU 1015-Safecraker(回溯,字符处理)

HDU 2676-Sudoku(抽象,回溯)

以下是一些DFS:

Sticks

http://acm.hdu.edu.cn/showproblem.php?pid=1455

http://acm.hdu.edu.cn/showproblem.php?pid=1455

http://acm.hdu.edu.cn/showproblem.php?pid=2553

http://acm.hdu.edu.cn/showproblem.php?pid=1426

http://acm.hdu.edu.cn/showproblem.php?pid=1528

http://acm.hdu.edu.cn/showproblem.php?pid=1175

http://acm.hdu.edu.cn/showproblem.php?pid=1010

http://acm.hdu.edu.cn/showproblem.php?pid=1312

http://acm.hdu.edu.cn/showproblem.php?pid=1716

http://acm.hdu.edu.cn/showproblem.php?pid=5547

http://acm.hdu.edu.cn/showproblem.php?pid=1181

http://acm.hdu.edu.cn/showproblem.php?pid=5305

记忆化搜索(DFS+DP):

http://acm.hdu.edu.cn/showproblem.php?pid=1978

http://acm.hdu.edu.cn/showproblem.php?pid=1078

http://acm.hdu.edu.cn/showproblem.php?pid=1428



版权声明:本文为qq_40511966原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。