几种暴力搜索方法
主要内容来自 刘汝佳 的 《算法竞赛入门经典》
1. 直接枚举
这个方法,主要是找到“一种有规则的方法”来枚举所有结果,保证结果完整即可。
经典例题:输入n,输出前 n 个数的所有排列。
提示:用递归,每次从当前排列中没有的数中进行展开,最后的结果是一棵树。至于如何找到排列中没有的数,比较挫的办法是每个数搜索一下看在不在,好一点的办法是用一个访问数组来保存。
收获:这类是用递归枚举的方法,都可以抽象成一棵搜索树,有的搜索树的每个节点都是一个解,有的搜索树只有叶子结点是解。根据泰勒展开式,上面的例题大概有 O(n!) 个节点。
2. 回溯法
其实这个方法我之前学 MCE 问题的 BK 算法就见识过,而且各种回溯条件都有。之所以叫“回溯”是因为如果碰到不需要再扩展的分支就回溯到它的父节点。
回溯法其实很简单,搜索树有一些无效分枝,这些分支如果我们可以提前判定,那么就不需要再扩展这个分支了。
经典例题:八皇后(自行google) 困难的串(Uva 129题目)
提示:一定要找到如何判断无效分枝的方法,这是回溯和普通暴力枚举最大的区别。
收获:善用访问数组,不管是1维,2维,还是多维。
3. 状态空间搜索
状态空间搜索相当于是一个“隐形”的图遍历问题,每个节点都可以看做是方案的一个状态,然后按照图遍历算法来搜索初始状态到最终解决方案的路径或者步数。关键是要有一个查找表,然后每次扩展新节点的时候,都检查查找表看是否是已经遍历过的节点,这样可以很好地减少问题规模。
经典例题:八数码 倒水问题
提示:如何组织一个查找表是比较重要的,可以用一个可能最多状态数目 maxn 大小的数组,也可以用编码(例如将一个状态压缩到一个int),也可以用hash,当然第1种和第3种是万能方法,第2种需要机缘巧合 :)
4. 迭代加深搜索(iterative deepening, IDA)
这个方法其实我没有完全搞懂,但是思想和回溯法差不多,只不过它的回溯条件比较特殊,它主要适用于搜索深度没有限制的问题,我们可以从小到大枚举深度上限 maxd,每次执行只考虑搜索到深度为 maxd 的节点,只要解的深度有限,那么可以很快搜索出。
经典例题:埃及分数 编辑书稿(Uva 11212 题目)
提示:IDA的重点在于确定一个比较好的启发函数,有点类似不等式放缩,即使最好的情况下拓展这个分支也得不到最优解,那么我们就不需要再拓展它了,直接回溯即可。
看了前面几章,感觉没啥暖用,但是这一章感觉却不太一样,感觉已经可以解决很大一部分算法题了。当然是对于我这种业余爱好者来说。
如果想要练习的话,刘汝佳给出的那个 AOAPC 其实不是很好用,推荐使用我科的 virtual OJ,里面有所有的 Uva 题目: https://vjudge.net/problem