深度优先搜索算法(英语:Depth-First-Search,简称DFS)是一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。(直到走不下去才往回走)
基本模板
1 int check(参数)
2 {
3 if(满足条件)
4 return 1;
5 return 0;
6 }
7
8 void dfs(int step)
9 {
10 判断边界
11 {
12 相应操作
13 }
14 尝试每一种可能
15 {
16 满足check条件
17 标记
18 继续下一步dfs(step+1)
19 恢复初始状态(回溯的时候要用到)
20 }
21 }
实例
1、全排列(字符串内元素的所有排列方法)(洛谷CF6A与此题类似)
1 //全排列问题
2 #include<stdio.h>
3 #include<string.h>
4
5 int n;
6 char a[15];
7 char re[15];
8 int vis[15];
9 //假设有n个字符要排列,把他们依次放到n个箱子中
10 //先要检查箱子是否为空,手中还有什么字符,把他们放进并标记。
11 //放完一次要恢复初始状态,当到n+1个箱子时,一次排列已经结束
12 void dfs(int step)
13 {
14 int i;
15 if(step==n+1)//判断边界
16 {
17 for(i=1;i<=n;i++)
18 printf("%c",re[i]);
19 printf("\n");
20 return ;
21 }
22 for(i=1;i<=n;i++)//遍历每一种情况
23 {
24 if(vis[i]==0)//check满足
25 {
26 re[step]=a[i];
27 vis[i]=1;//标记
28 dfs(step+1);//继续搜索
29 vis[i]=0;//恢复初始状态
30 }
31 }
32 return ;
33 }
34
35 int main()
36 {
37 int T;
38 scanf("%d",&T);
39 getchar();
40 while(T--)
41 {
42 memset(a,0,sizeof(a));
43 memset(vis,0,sizeof(vis));//对存数据的数组分别初始化
44 scanf("%s",a+1);
45 n=strlen(a+1);
46 dfs(1);//从第一个箱子开始
47 }
48 return 0;
49 }
2、棋盘问题http://acm.sdtbu.edu.cn/vjudge/contest/view.action?cid=2373#problem/A
问题:在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
input: 输入含有多组测试数据。 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 当为-1 -1时表示输入结束。 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
output:对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
1 #include <stdio.h>
2 #include <string.h>
3
4
5 char a[10][10];//棋盘
6 int vis[10];//标记
7 int n, k, js = 0;//n行列数k棋子数js方案数
8
9 void dfs(int, int);
10
11 int main()
12 {
13 while(EOF != scanf("%d%d", &n, &k)) {
14 if(n == -1 && k == -1) break;
15 memset(vis, 0, sizeof(vis));
16 getchar();
17 for(int i = 0; i < n; i++) {
18 gets(a[i]);
19 }
20 dfs(0, 0);//从第0行开始找, 已排0个棋子
21 printf("%d\n", js);
22 js = 0;
23 }
24
25 return 0;
26 }
27
28 void dfs(int r, int step) {
29 if(step == k) {//判断边界,此时棋子已经放完
30 js++;
31 return;
32 }
33 for(int i = r; i < n; i++) {
34 for(int j = 0; j < n; j++) {
35 if (a[i][j] == '#' && !vis[j]) {//是棋盘区且没有放棋子
36 vis[j] = 1;//表示本列已经放了棋子
37 dfs(i + 1,step + 1);//直接从下一行开始,因为不能放在同一行
38 vis[j] = 0;//回溯
39 }
40 }
41 }
42 }
类似排列组合
总结一下,用递归法来实现DFS,比较好理解,就一直往下找,知道走不通后在回来尝试其它的地方。一个DFS一般要判断边界,check来判断是否符合相应条件,vis或者book来记录是否已经被用过,递归进行下一步操作。有的时候我们要将标记过的点恢复原来的状态,有时候则不必要恢复(油田问题),要结合具体的问题来分析。
恢复标记相当于回溯的思想。
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
参考博客raphealguo@CSDN:https://blog.csdn.net/ldx19980108/article/details/76324307
来源:https://www.cnblogs.com/knightoflake/p/12527460.html