今天又复习了图的深度优先搜索,深深感觉了深搜就是外挂,岂止是外挂,简直就是外挂,动态规划做不出来的,深搜搜出来了,贪心贪不出来的深搜搜出来了,连并查集,拓扑排序做不出来的,深搜都做出来了,很遗憾以前深搜没有好好学。
深度优先搜索(Depth-First-Search)是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.
深搜主要的是一个回溯的过程,标记当前的点已经用过,在用过的基础上,进行下一步,当进行到了最后发现此点不能用,于是就回溯回来,因此造成了该算法的时间复杂度为(O(n!));很容易造成超时,但是在部分题内,盲目搜索遍历一遍也挺挺快的,基本都是0ms,此算法,代码容易写,容易想,怪不得研究出dfs的两位神得了图灵奖,真6;
oj上边有很多适合练习深搜的题,如hdoj1045,还有最经典的n皇后问题,hdoj1258,南阳32组合数问题,南阳oj58最小步数问题,hdoj1016素数环问题,poj2362,zoj1003...(由于题目过多,只列取了经典的);
附几道深经典搜题及代码:
组合数问题:
描述
-
输入
- 输入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:
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
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,素数环问题,约瑟夫环
:
Note: the number of first circle should always be 1.
You are to write a program that completes above process.
Print a blank line after each case.
6 8
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皇后问题:
你的任务是,对于给定的N,求出有多少种合法的放置方法。
1 8 5 0
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;
}