图的深度优先搜索(DFS)

今天又复习了图的深度优先搜索,深深感觉了深搜就是外挂,岂止是外挂,简直就是外挂,动态规划做不出来的,深搜搜出来了,贪心贪不出来的深搜搜出来了,连并查集,拓扑排序做不出来的,深搜都做出来了,很遗憾以前深搜没有好好学。

深度优先搜索(Depth-First-Search)是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。


事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.

深搜主要的是一个回溯的过程,标记当前的点已经用过,在用过的基础上,进行下一步,当进行到了最后发现此点不能用,于是就回溯回来,因此造成了该算法的时间复杂度为(O(n!));很容易造成超时,但是在部分题内,盲目搜索遍历一遍也挺挺快的,基本都是0ms,此算法,代码容易写,容易想,怪不得研究出dfs的两位神得了图灵奖,真6;

oj上边有很多适合练习深搜的题,如hdoj1045,还有最经典的n皇后问题,hdoj1258,南阳32组合数问题,南阳oj58最小步数问题,hdoj1016素数环问题,poj2362,zoj1003...(由于题目过多,只列取了经典的);

附几道深经典搜题及代码:

组合数问题:

描述
找出从自然数1、2、... 、n(0<n<10)中任取r(0<r<=n)个数的所有组合。
输入
输入n、r。
输出
按特定顺序输出所有组合。
特定顺序:每一个组合中的值从大到小排列,组合之间按逆字典序排列。
样例输入
5 3
样例输出
543
542
541
532
531
521
432
431
421
321

附代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,vis[1000],a[1000]={1000};
void dfs(int step)
{
	int i;
	if(step==n+1)
	{
		for(i=1;i<n;i++)
		printf("%d",a[i]);
		printf("%d\n",a[i]);	
	}
	for(i=m;i>=1;--i)
	{
		if(!vis[i]&&i<a[step-1])
		{
			vis[i]=1;
			a[step]=i;
			dfs(step+1);
			vis[i]=0;
		}
	}
}
int main()
{
	while(scanf("%d%d",&m,&n),m+n)
	{
		memset(vis,0,sizeof(vis));
		dfs(1);
	}
	return 0;
}

杭电1258:

Problem Description
Given a specified total t and a list of n integers, find all distinct sums using numbers from the list that add up to t. For example, if t=4, n=6, and the list is [4,3,2,2,1,1], then there are four different sums that equal 4: 4,3+1,2+2, and 2+1+1.(A number can be used within a sum as many times as it appears in the list, and a single number counts as a sum.) Your job is to solve this problem in general.
 

Input
The input will contain one or more test cases, one per line. Each test case contains t, the total, followed by n, the number of integers in the list, followed by n integers x1,...,xn. If n=0 it signals the end of the input; otherwise, t will be a positive integer less than 1000, n will be an integer between 1 and 12(inclusive), and x1,...,xn will be positive integers less than 100. All numbers will be separated by exactly one space. The numbers in each list appear in nonincreasing order, and there may be repetitions.
 

Output
For each test case, first output a line containing 'Sums of', the total, and a colon. Then output each sum, one per line; if there are no sums, output the line 'NONE'. The numbers within each sum must appear in nonincreasing order. A number may be repeated in the sum as many times as it was repeated in the original list. The sums themselves must be sorted in decreasing order based on the numbers appearing in the sum. In other words, the sums must be sorted by their first number; sums with the same first number must be sorted by their second number; sums with the same first two numbers must be sorted by their third number; and so on. Within each test case, all sums must be distince; the same sum connot appear twice.
 

Sample Input
   
   
4 6 4 3 2 2 1 1 5 3 2 1 1 400 12 50 50 50 50 50 50 25 25 25 25 25 25 0 0
 

Sample Output
   
   
Sums of 4: 4 3+1 2+2 2+1+1 Sums of 5: NONE Sums of 400: 50+50+50+50+50+50+25+25+25+25 50+50+50+50+50+25+25+25+25+25+25
 
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace  std;
int a[10100];
int res[10100]={10000};
int vis[10100],j,flag,n;
void dfs(int m,int cnt)
{
	int i;
	if(m==0)
	{
		flag=1;
		for(j=1;j<cnt-1;j++)
		printf("%d+",res[j]);
		printf("%d\n",res[j]);
		return ;
	}
	for(i=1;i<=n;i++)
	{
		if(!vis[i]&&m-a[i]>=0&&a[i]<=res[cnt-1])
		{
			vis[i]=1;
			res[cnt]=a[i];
			dfs(m-a[i],cnt+1);
			vis[i]=0;
			while(a[i]==a[i+1]&&i<=n)
			++i;
		}
	}
}
int main()
{
	int m,i,j;
	while(scanf("%d%d",&m,&n),(m||n))
	{
		for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
		
		flag=0;
		printf("Sums of %d:\n",m);
		memset(vis,0,sizeof(vis));
		dfs(m,1);
		if(!flag)
		printf("NONE\n");
	}
}
杭电1016,素数环问题,约瑟夫环

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
 

附代码 :

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int vis[30],a[30],m,n;
int is(int n)
{
	for(int i=2;i*i<=n;i++)
	if(n%i==0)
	return 0;
	return 1;
}
void dfs(int n,int c)
{
	if(c==n)
	{
		if(is(a[n]+1))
		{
			printf("1");
			for(int i=2;i<=n;i++)
			printf(" %d",a[i]);
			printf("\n");
		}
		return ;
	}
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]&&is(a[c]+i))
		{
		a[c+1]=i;
		vis[i]=1;
		dfs(n,c+1);
		vis[i]=0;
		}
	}
}
int main()
{
	int flag=1;
	while(~scanf("%d",&n))
	{
		memset(vis,0,sizeof(vis));
		printf("Case %d:\n",flag++);
		vis[1]=1;
		a[1]=1;
		dfs(n,1);
		printf("\n");
	}
	return 0;
}
杭电2553,n皇后问题:

Problem Description
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。


Input
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
 

Output
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
 

Sample Input
   
   
1 8 5 0
 

Sample Output
   
   
1 92 10
附代码:

#include<stdio.h>  
#include<math.h>  
int x[15];//x数组表示放在该列的哪个位置,下标表示列,值表示放置在第几行   
int sum, n;  
bool place (int v)  
{  
    int i;  
    for(i=1;i<v;++i)  
    {  
        if(x[i] == x[v] || abs(x[i]-x[v])==abs(i-v))//如果在同一行,或在对角线方向(为什么这里不判断是否为同一列呢?主要是因为x数组表示放在该列的哪个位置)   
            return 0;  
    }  
    return 1;  
}  
void backtrack(int v)  
{  
    int i;  
    if(v>n) //把最后一个皇后放置成功后   
        sum++;  
    else  
    {  
        for(i=1;i<=n;++i) //找n个皇后   
        {  
            x[v]=i;  
            if(place(v))  
            {  
                backtrack(v+1);  
            }  
        }  
    }  
}  
int main()  
{     
    int ans[15];  
    for(n = 1; n <= 10; ++n)  
    {  
        sum = 0;  
        backtrack(1);  
        ans[n] = sum;     
    }     
    while(scanf("%d",&n), n)  
    {  
        printf("%d\n",ans[n]);  
    }  
    return 0;  
}  






  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值