DFS(深度优先搜索)

假如想象 提交于 2020-03-20 00:03:54

深度优先搜索算法(英语: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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!