深度优先搜索的优化技巧
1、优化搜索顺序
在一些搜索问题中,搜索树的各个层次,各个分支之间的顺序不是固定的。不同的搜索顺序会产生不同的搜索树形态,其规模大小也相差甚远。
2、排除等效冗余
在搜索过程中,如果我们能够判定从搜索树的当前节点上沿着某几条不同分支到达的子树是等效的,那么只需要对其中的一条分支执行搜索。
3、可行性剪枝
在搜索过程中,及时对当前状态进行检查,如果发现分支已经无法到达递归边界,就执行回溯。这好比我们在道路上行走时,远远看到前方是一个死胡同,就应该立即折返绕路,而不是走到路的尽头再返回。
某些题目条件的范围限制是一个区间,此时可行性剪枝也被称为”上下界剪枝“
4、最优化剪枝
在最优化问题的搜索过程中,如果当前花费的代价已经超过当前搜到的最优解,那么无论采取多么优秀的策略到达递归边界,都不可能更新答案。此时可以停止对当前分支的搜索,执行回溯。
5、记忆化
可以记录每个状态的搜索结果,在重复遍历一个状态时直接检索并返回。这好比我们对图进行深度优先遍历时标记一个节点是否已经被访问过。
【例题1】数的划分(可行性剪枝,上下界剪枝)
题目描述
输入
输出
样例输入
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 }