深度优先搜索的优化技巧

1、优化搜索顺序

  在一些搜索问题中,搜索树的各个层次,各个分支之间的顺序不是固定的。不同的搜索顺序会产生不同的搜索树形态,其规模大小也相差甚远。

2、排除等效冗余

  在搜索过程中,如果我们能够判定从搜索树的当前节点上沿着某几条不同分支到达的子树是等效的,那么只需要对其中的一条分支执行搜索。

3、可行性剪枝

  在搜索过程中,及时对当前状态进行检查,如果发现分支已经无法到达递归边界,就执行回溯。这好比我们在道路上行走时,远远看到前方是一个死胡同,就应该立即折返绕路,而不是走到路的尽头再返回。

  某些题目条件的范围限制是一个区间,此时可行性剪枝也被称为”上下界剪枝“

4、最优化剪枝

  在最优化问题的搜索过程中,如果当前花费的代价已经超过当前搜到的最优解,那么无论采取多么优秀的策略到达递归边界,都不可能更新答案。此时可以停止对当前分支的搜索,执行回溯。

5、记忆化

  可以记录每个状态的搜索结果,在重复遍历一个状态时直接检索并返回。这好比我们对图进行深度优先遍历时标记一个节点是否已经被访问过。


 

 

【例题1】数的划分(可行性剪枝,上下界剪枝)

题目描述

  积极探索太空的回报是丰厚的,因为魔法世界的科学家后来通过挖掘史前文明遗址发现,他们所在的星球,每隔一亿五千万年左右就会发生一次由天外陨石或其他意外事件引发的物种大灭绝。称霸星球一亿多年的恐龙,就是因此而灭绝的。只有积极进取,锐意创新的宇宙文明,才有可能躲过这场“天罚”。现在,天顶星人对魔法世界发动了大规模陨石攻击,魔法世界的太空防御系统及时作出反应,将防御能量n分成k份以对付k个陨石,已知每份不能为空(否则会被陨石击中),任意两份不能相同(不考虑顺序)。例如:n=7,k=3,下面三种分法被认为是相同的:1,1,5; 1,5,1; 5,1,1。问有多少种不同的分法。

输入

n,k (6<n≤200,2≤k≤6)

输出

一个整数,即不同的分法。

 

样例输入

7 3

样例输出

4


【思路】:

  本题就是求把数字n无序划分成k份的方案数。也就是求方程x1+x2+……+xk = n,1<=x1<=x2<=……xk的解数。

  搜索的方法是依次枚举x1,x2……xk的值,然后判断。如果这样直接搜索,程序的运行速度是非常慢的。但由于本题的数据规模比较小,如果控制好扩展结点的“上界”和“下界”,也是能够很快得出解的。

  约束条件:

  1、由于分解数不考虑顺序,因此我们设定分解数依次递增,所以扩展结点时的“下界”应是不小于前一个扩展结点的值,即a[i-1]<=a[i]

  2、假设我们将n已经分解成了a[1]+a[2]+……+a[i-1],则a[i]的最大值为将i~k这k-i+1份平均划分,即设m=n-(a[1]+a[2]+……+a[i-1]),则a[i]<=m/(k-i+1),所以扩展结点的“上界”为 m/(k-i+1)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,ans;
 4 int a[10];
 5 void dfs(int k){            //分第k份
 6     if( n==0 ) return ;
 7     if( k == m ) {
 8         if( n >= a[k-1] ) 
 9             ans ++;
10         return ;
11     }
12     for (int i=a[k-1];i<=n/(m-k+1);i++){    //第k份的上下界
13         a[k] = i ;                          //第k份的值
14         n-=i;
15         dfs(k+1);
16         n+=i;
17     }
18 }
19 int main()
20 {
21     scanf("%d%d",&n,&m);
22     a[0] = 1 ;                      //初始值起步为1
23     dfs(1);
24     printf("%d\n",ans);
25     return 0;
26 }
数的划分

相关文章: