【算法】深度优先搜索(DFS)I

来源:互联网 发布:mac win10 iso 下载 编辑:程序博客网 时间:2024/04/28 04:31

1. 算法描述


DFS(depth first search)的算法思想非常朴素:对点u,访问与u相连的某点u1,再访问与u1相连的某点u2,……依次由浅入深地访问相连节点。基本步骤如下:

(1)访问点u;

(2)若与点u相连的点u1未被访问,则递归地访问u1的相连节点。


DFS的递归实现:

void dfs(int u,struct ALGraph *gra){struct ArcNode *p;visit[u]=1;for(p=gra->Gvertex[u].firstarc;p!=NULL;p=p->next)if(!visit[p->adjvex])dfs(p->adjvex,gra);}

代码中,图是用邻接表存储。如果用栈来维护已访问的节点,DFS有非递推实现:


2. 应用


DFS无论是在图论中,还是在其他问题中都有着非常广泛的应用,常见的有:


2.1 判断两点之间的可达性


由点u开始,经过DFS遍历后,若点v未被访问,则说明点u到点v不可达;反之,则否。问题有,

(1)POJ 3256   求点x满足图中的k个点对其均可达。


2.2 生成组合


组合,就是在n个数中取k个数,共有C(n, k)种取法。如果要遍历这C(n, k)种取法,是一个比较困难的问题,可以用DFS加以模拟。问题有

(1)POJ 3628   n个数中取若干个数相加,满足相加之和大于B,求相加之和为最小的那一个组合。

(2)POJ 2245   给出k个数,遍历c(k, 6)的组合。

(3)POJ 1564   给出有重复的n个数,选取若干数使得和为t,求所有的选取情况。

(4)POJ 1011   将n个数分成若干个组,且每组内的数相加之和为x,求最小的x。

(5)POJ 2362   给一些棒子,求能否摆成一个正方形。

(6)POJ 1040   带限制条件,求从t个订单选出若干个订单使得收益最大。

(7)POJ 2531   对分属两个集合的节点,求如何分配集合使得两两之间距离之和的最大值。


2.3 生成排列


排列,就是在n个数中有顺序地取出k个数,共有A(n, k)种取法。当取n个数时,称该问题为全排列问题。问题有

(1)POJ 2907   遍历n个点的全排列,求最短路径。

(2)POJ 1256   求带重复元素的全排列。

(3)POJ 1270   拓扑排序。

(4)POJ 2367   带限制条件的全排列。


2.4 求连通分支


从某一点开始DFS,直至结束,此为一个连通分支;再从另一个未被访问的点开始DFS……即可求出连通分支数。问题有POJ 2386 1562 。


2.5 遍历


暴力遍历所有可能情形,可用来解决“骑士游历”、“八皇后问题”、“数独问题”。其中,后两个问题的解决可以用DLX算法进行优化。


3. Referrence


[1] Felicia Crazy, Powerful Algorithm - DFS.


4. 问题


4.1 POJ 3256


题目大意:图中的k个点对点x均可达,求满足这样可达性条件的点x有多少个。


思路:DFS可用来判断两点之间的可达性。但是,如果依次判断图中每个点是否满足可达性条件,那么算法复杂度较高。可以对DFS稍作修改:用num[ i ]记录点i在DFS中被访问的次数;k个点DFS之后,如果num [ i ]==k,则说明k个点对点i均可达。


源代码:

3256Accepted504K79MSC1370B2013-10-05 15:07:13

#include "stdio.h"#include "string.h"#define MAXK 100#define MAXN 1001struct ArcNode  {int adjvex;struct ArcNode *next;};struct VNode {int vertex;struct ArcNode *firstarc;};typedef struct ALGraph{int k,n,m;struct VNode Gvertex[MAXN];}ALGraph;int cows[MAXK],visit[MAXN],num[MAXN];void CreateGraph(ALGraph *gra){int k,n,m;int i,start,end;struct ArcNode *p=NULL;scanf("%d%d%d",&k,&n,&m);gra->k=k; gra->n=n; gra->m=m; for(i=0;i<k;i++)scanf("%d",&cows[i]);for(i=1;i<=n;i++){gra->Gvertex[i].vertex=i;gra->Gvertex[i].firstarc=NULL;}for(i=1;i<=m;i++){scanf("%d%d",&start,&end);        p=(struct ArcNode *) malloc(sizeof(struct ArcNode));p->adjvex=end;p->next=gra->Gvertex[start].firstarc;gra->Gvertex[start].firstarc=p;}}void dfs(int u,struct ALGraph *gra){struct ArcNode *p;visit[u]=1;num[u]++;for(p=gra->Gvertex[u].firstarc;p!=NULL;p=p->next)if(!visit[p->adjvex])dfs(p->adjvex,gra);}int main(){int i,count=0;ALGraph *gra=(ALGraph *) malloc(sizeof(ALGraph));CreateGraph(gra);memset(num,0,sizeof(num));for(i=0;i<gra->k;i++){memset(visit,0,sizeof(visit));dfs(cows[i],gra);}for(i=1;i<=gra->n;i++)if(num[i]==gra->k)count++;printf("%d\n",count);}

4.2 POJ 3628


题目大意:给定一个数B和一组数h[ ],h[ ]满足其中部分数相加之后大于B,求在比B大的所有组合中最小的那一个。


基本的DFS,遍历所有的相加可能情形,求出最小的那一个。


源代码:

3628Accepted164K32MSC542B2013-10-05 21:29:13

#include "stdio.h"#include "stdlib.h"#include "limits.h"int cmp(const void *a,const void *b){return (*(int *)b-*(int *)a);}int N,B,h[20],minimum;void dfs(int depth,int sum){if(depth==N+1||minimum==0)return;if(sum>=B)minimum=minimum<sum-B ? minimum: sum-B;dfs(depth+1,sum);dfs(depth+1,sum+h[depth]);}int main(){int i;minimum=INT_MAX;scanf("%d%d",&N,&B);for(i=0;i<N;i++)scanf("%d",&h[i]);qsort(h,N,sizeof(int),cmp);dfs(0,0);printf("%d\n",minimum);return 0;}

4.3 POJ 2245


题目大意:给出k个数,从k个数取6个数,求生成c(k, 6)的组合。


标准的DFS,用到了剪枝。语句dfs(depth+1,num+1);  与语句 dfs(depth,num+1); 的顺序不能颠倒。


源代码:

2245Accepted164K16MSC494B2013-10-06 15:05:38

#include "stdio.h"#include "stdlib.h"int k,subset[12],result[6];void dfs(int depth,int num){int i;if(num>k||k-num+depth<6)  //剪枝,判断剩下的数是否还足以拼成return;if(depth==6){for(i=0;i<5;i++)printf("%d ",result[i]);printf("%d\n",result[5]);return;}result[depth]=subset[num];dfs(depth+1,num+1);dfs(depth,num+1);}int main(){int i;while(scanf("%d",&k)&&k){for(i=0;i<k;i++)scanf("%d",&subset[i]);dfs(0,0);printf("\n");}return 0;}



原创粉丝点击