本文是作者自己写的心得,是入门不久的心得。大牛可直接无视~
DFS是ACM的初级算法、也是学习递归思想和回溯思想最有用的算法。关于递归、搜索及DFS的介绍,参见:http://baike.baidu.com/view/96473.htm、http://baike.baidu.com/view/3688332.htm 、http://baike.baidu.com/view/973383.htm 。
个人认为DFS搜索其实是将枚举算法和回溯算法有效的结合在一起。其搜索过程实际上是根据初始条件和扩展规则构造一棵解答树并寻找符合目标状态的节点的过程。(解答树的引入对理解递归过程非常有用,解答树内容详见刘如佳《入门经典》P121)。
下面结合个人所做的一道简单题目,对DFS算法进行总结:
Problem Description
A ring is compose of n circles as shown in diagram. Put natural number 1, 2, ..., n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.Note: the number of first circle should always be 1.
Input
n (0 < n < 20).
Output
The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.You are to write a program that completes above process.Print a blank line after each case.
Sample Input
6
8
Sample Output
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4
Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2
题目其实是让你输出一个排列,使得这个排列为一个圆环时相邻两个数的和为素数。如果我们使用枚举排列的方法,不难发现其排列总数高达20!,显然超时。这时候我们发现回溯的好处,至少它比枚举快的多(因为当我们遇到第一个不符合条件的解时,便返回到了初始的状态位置,这样避免了无用的后续判断)。
对于递归规过程的理解,只要借助DFS存储数据的特点来看就行。DFS是运用栈来存放已经搜索过的结点。我们把这个栈想象成一个倒过来的堆(如下图)、那么这样一来递归过程就容易多了。(上下行数字之间连线)
…… …… ……
3 4 5 6 7 (放3后不可以) 2 3 5 6 7 (放5后不可以) 2 3 4 5 7 (放7后不可以)
2 3 4 5 6 7
1
个人觉得要做好DFS的题目,只要处理好两个问题即可:
1. 边界的判断、即何时退出递归过程。我们只要分析出何时(什么情况下)能得到一组可行解、或者当运行到这种情况下时已经无法继续递归下去了。那么此时就是递归的边界。
2. 数据初始化的问题、这也是我常犯的一个错误。在使用vis[]进行数据初始化时要谨记一轮递归过后要将其还原(即出栈)。另外,第一个元素的初始化要仔细分析,切记不要忘掉。
下面是这道题目的代码及分析:
#include<cstdio> #include<algorithm> using namespace std; #define N 1000 int num,ans,buf[N]; bool vis[N]; int is_prime(int x,int y) { int sum=x+y; for(int i=2;i*i<=sum;i++) if(sum%i==0) return 0; return 1; } void dfs(int cur) { if(cur>num && is_prime(buf[num],buf[1])) { ans++; for(int i=1;i<=num;i++) printf(i==1?"%d":" %d",buf[i]); printf("/n"); return ; } else for(int i=2;i<=num;i++) { buf[cur]=i; if(!vis[i] && is_prime(buf[cur],buf[cur-1])) { vis[i]=true; dfs(cur+1); vis[i]=false; } } } int main() { while(scanf("%d",&num)!=EOF) { memset(vis,false,sizeof(vis)); ans=0; buf[1]=1; dfs(2); printf("个数:%d/n",ans); } return 0; }