基础算法与数学总结 【第三部分 搜索】
基础算法与数学总结 【第三部分 搜索】
1 BFS与DFS
搜索的基础应用略。
这里列举几个浅显的概念以及性质,这些会有很多用处:
-
DFS序中一棵子树对应根节点的两次访问中间的区间
-
树的重心是使得去除后分出的最大一棵子树最小的点,直观理解就是平均分配
-
BFS基本性质:”两段性“、”单调性“
-
Topo排序性质:对于边(x,y),x在Topo排序中出现在y之前
2 搜索剪枝与优化
剪枝就是剪去搜索树上的纸条,而搜索优化更偏向于搜索策略的优化。这里我们合并到一起讲。
2.1 剪枝
剪枝有以下策略:
-
优化搜索顺序
如果可以调整,使得先搜索可以更快达到边界的状态,就能更快发现答案而不是在深层子树上消耗时间。
这类优化比较”玄学“,因为其实并不一定就可以优化,而且很多时候都是我们凭着模拟结果猜测的策略。
-
排除等效冗余
有的状态可以把他们看作相同的,只用搜一个就可以了。书上有一个提示,其实我在DP总结也提到类似的,DP如果混淆了”决策“与”阶段“就肯定做不出来,导致要重复考虑无限的细节,而那些应该是子问题考虑过的。
在搜索里,就是不要混淆”层次“与”分支“,每次只应该多确定一步,否则导致重复遍历大量搜索树。
设计这种优化,要关注重复的状态进行优化。
-
可行性剪枝
如果提前预料当前分支不能搜到合法解,直接回溯。
设计此类剪枝,要关注题目中对于答案的限制与隐含限制,及时进行判断
-
最优性剪枝
如果当前花费的代价已经大于答案,直接回溯
注意代价的增长是否是单调的?如果不是,只能继续搜索。
-
记忆化
记录每个状态的搜索结果,如果重复搜到,再进行调用。
这也是DP的一种实现方法。
2.2 搜索优化
BFS和DFS的搜索优化几乎是一致的,这里统一说明,以下:
-
迭代加深(DFS)
如果一开始搜索就选错了分支那么就可能在深层子树浪费时间,如果对于搜索深度进行限制,那么可以避免这种情况。
但是应用比较少,因为如果答案真的很深那么这样是浪费时间的。通常是作为IDA*的实现框架
-
双向搜索(BFS/DFS)
如果问题具有明确的初态终态,那么不妨分别从两边开始搜索再”meet in middle“,这样搜索的层数减半。
当然也有另一种解释就是把问题规模分成两半然后通过组合其他算法实现高效合并到终态。例题一道见下。
3 广搜变形
3.1 双端队列BFS
双端队列BFS用于处理边权1/0的情况。请注意,它应该也不能处理负边权,因为一般的问题中我们都是默认代价是正的,可一旦代价有负的,就只能迭代了。
普通BFS中,代价都是1,此时”单调性“就是访问阶段。
3.2 优先队列BFS
类似双端队列BFS,这就是Dijkstra算法。
4 Astar和IDAstar
4.1 Astar
这是加了个估价函数的BFS,试图选择更优的转移决策。
但是要满足,对于任意状态,估价函数的估值不应该大于实际代价,否则正确的方案可能被估计太大的代价而无法被扩展。但如果满足这样的条件,总会在某一刻最优解会得到优势。
所以Astar的艺术就是设计估价函数,只要这个值越接近实际值,那么就可以更快地扩展到最优解。
一个好的估价函数应该顺应实际的代价的变化趋势。
4.3 IDAstar
类比以下,这就是加了估价函数的迭代加深DFS。迭代加深的意义是防止估价错误带来的复杂度爆炸。
5 例题
一次只能扩展一步吗?虽然这个题把上一步的方向加入状态也可以做,但是如果一次就扩展同一方向的所有节点,那么是不是就避免了这个问题呢?
灰点限制很多,其他两种点都只需要遍历过去然后涂不同颜色就可以了。可以证明,只要是超过1个点的联通无向图,必能进行这种染色,这是显然的。但是如果只有一个点,那么涂什么都没办法。所以可以不涂灰点。
- 171. 送礼物 - AcWing题库 折半搜索
将礼物分成两半,尝试在第二次搜索的可达集合中寻找第一次也可达的,这就是“meet in middle”的实际意义。
这道题要分类讨论的情况很多,所以不妨先列出来,这样条理会清晰很多。
通过及时判断一些不可能的情况进行可行性剪枝。
从终点开始反向先处理出可以看到终点的点作为终止点。
这种题目没有说明具体行数/列数限制,只给了格数,可以使用把矩阵化成一行的方法。
这是一道很老的题目了,但是却阐明了状态的含义。
状态一定是这样的变量:
-
对答案有贡献
-
构成条件限制
这道题有一个血量,这意味着走到同一个格子时面临的状态是不同的,要加上血量才是完整的。
显然每走一步要么不用额外旋转,要么需要旋转一次代价为1。这就是双端队列搜索。
有男孩、女孩可以一起走,还要求要相遇,明显的双向奔赴——用双向BFS
这一个题目进一步让我们领会了BFS的特性。
如果把人、箱拆开单独做是不行的,这两个是完全不同的单位,一个可以自己走,另一个需要推,这就意味着把他们放进一个队列会破坏单调性。
但是我们发现,人不推箱子移动的时候只有可能是在试着走到箱子的另一边,所以我们可以把人的移动单独来看,这就好比拆分了另一个“走地图”问题出来。
lyd提醒我们,在设计算法的时候,不妨设细节部分已经解决。
其实笔者读至此处,感受颇深。例如上面我们提到“排除等效冗余”和之前提到动态规划的失败,都是因为没有秉持模块化的原则,违背了算法优化的基本原理。
Astar算法的艺术,就是设计优秀的估价函数,上面我们说,一个好的估价函数要适应实际代价的变化趋势。
设计这样的估价函数,要点就是:模拟搜索过程,找到每一步的“理想状态”,看最最理想的情况下需要的代价。
IDAstar与上面的操作大同小异,这里比较以下IDAstar与Astar的适用场景:
-
Astar:扩展消耗的代价不固定、答案层次深每步可选后继少、“走地图”问题
-
ID-Astar:答案层次较浅、搜索树增长快
本文来自博客园,作者:haozexu,转载请注明原文链接:https://www.cnblogs.com/haozexu/p/18281758

浙公网安备 33010602011771号