剪枝
剪枝是搜索常用的优化手段,常常能把指数级的复杂度,优化到近似多项式的复杂度。
01
剪枝概述
剪枝的关键在于剪枝的判断:什么枝该剪、在什么地方减。
BFS的剪枝通常是判重,如果搜索到某一层时,出现重复的状态,就剪枝。例如经典的八数码问题,核心问题就是去重,把曾经搜过的八数码的组合剪去。
DFS的剪枝技术较多,有可行性剪枝、最优性剪枝、搜索顺序剪枝、排除等效冗余、记忆化搜索等等。
可行性剪枝:对当前状态进行检查,如果当前条件不合法就不再继续,直接返回。
搜索顺序剪枝:搜索树有多个层次和分支,不同的搜索顺序会产生不同的搜索树形态,复杂度也相差很大。
最优性剪枝:在最优化问题的搜索过程中,如果当前花费的代价已超过前面搜索到的最优解,那么本次搜索已经没有继续进行下去的意义,此时停止对当前分支的搜索进行回溯。
排除等效冗余:搜索的不同分支,最后的结果是一样的,那么只搜一个分支就够了。
记忆化搜索:在递归的过程中,有许多分支被反复计算,会大大降低算法的执行效率。用记忆化搜索,将已经计算出来的结果保存起来,以后需要用到的时候直接取出结果,避免重复运算,从而提高了算法的效率。记忆化搜索一般在DP中讲解,参考《算法竞赛入门到进阶》“7.2 递推与记忆化搜索”的例题poj 1163 “The triangle”、“7.5 数位DP”的例题hdu 2089 “不要62”。
一个题目中可能用到多种剪枝技术,请通过下面的例题掌握剪枝。
02
例题
下面的例题,难度逐渐增加。
❶ poj 3278
Catch That Cow
http://poj.org/problem?id=3278
题目描述:在一根直线上,奶牛在K位置,农夫在N位置。农夫想抓到牛,他有3种移动方法,例如他在X位置,他可以移动到X-1、X+1、2X的位置。问农夫最快要移动多少次,能从N到达K。0 ≤ N, K ≤ 100,000。
题解:
从N到K的最短路径问题,显然用BFS,每一步有3个分支。
可行性剪枝:如果农夫当前位置大于k,那么农夫只能不断做X-1操作,而不能使用变大的X+1、2X这2种操作。
❷ 洛谷 P1118
数字三角形
https://www.luogu.com.cn/problem/P1118
题目描述:写出一个1至N的排列ai,然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。下面是一个例子:
最后得到16这样一个数字
现在倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列ai,为1至N的一个排列。若答案有多种可能,则输出字典序最小的那一个。n≤12, sum≤12345。
题解:
(1)暴力法。对1~N这N个数做从小到大的全排列,对每个全排列进行三角形的计算,判断是否等于N。
对每个排列进行三角形计算,需要O(N2)次。例如第1行有5个数{a,b,c,d,e},那么第2行计算4次,第3行计算3次…等等,总次数是O(N2)的。
共有N!=4亿个排列,总复杂度是O(N!N2)的,显然会超时。
(2)三角计算优化+剪枝。
1)三角计算的优化。对排列进行三角形计算,并不需要按部就班地算,比如{a,b,c,d,e}这5个数,直接算最后一行的公式a+4b+6c+4d+e就好了,复杂度是O(N)的。不同的N有不同的系数,比如5个数的系数是{1,4,6,4,1},提前算出所有N的系数备用。可以发现,这些系数正好是杨辉三角。
2)剪枝。即使有了杨辉三角的优化,总复杂度还是有O(N!N)O(N!N),所以必须进行最优性剪枝。对某个排列求三角形和时,如果前面几个元素和已经大于sum,那么后面的元素就不用再算了。例如,N=9时,计算到排列{2,1,3,4,5,6,7,8,9},如果前5个元素{2,1,3,4,5}求和已经大于sum,那么后面的{6,7,8,9}~{9,8,7,6}都可以跳过,下一个排序从{2,1,3,4,6,5,7,8,9}开始。本题sum≤12345,和不大,