算法提高课——搜索
BFS
- 求最小
- 基迭代,不会爆栈
Flood fill算法:
可以在线性时间复杂度内,找到某个点所在的连通块。
//Home键到行首,End键到行尾
AcWing 1097. 池塘计数
AcWing 1098. 城堡问题
AcWing 1106. 山峰和山谷
最短路模型:
所有边权相等时,可以在线性时间内得到单源或多源最短路(可视为特殊的dijkstra)
AcWing 1076. 迷宫问题
AcWing 188. 武士风度的牛
AcWing 1100. 抓住那头牛
AcWing 173. 矩阵距离
- 保证任意时刻搜索队列中的值都具有
- 两段性 //最多有两段,但不一定是两段
- 单调性
最小步数模型:
用哈希(unoreded_map)或康托展开
AcWing 1107. 魔板
双端队列广搜:
用deque(front、push_front、pop_front、push_back)
AcWing 175. 电路维修
每个节点可能被更新(入队)多次。当终点第一次被扩展(出队)时,就能得到从起点到终点的最短距离。
//奇点:横纵坐标之和是奇数的点
偶点:横纵坐标之和是偶数的点
//‘\’是转义字符,因此需要打两个;
字符数组最后还有‘\n’,不要忘记
e.g. char cs[5]=“\\/\\/”;
双向广搜:
极大地缩小搜索的空间范围,提高搜索的效率。
状态空间数量一般为指数级。
适用于「最小步数模型」,但一般不用于数据较小的「flood fill算法」或「最短路模型」。
优化:每次选择当前元素数量较小的队列进行扩展
AcWing 190. 字串变换
//返回字符串t的一个子串:
i:起始下标
a[j].size():子串长度(若超过字符串t的长度则输出到结尾,若不写也直接输出到结尾)
翻转一个vector:。翻转一个数组,元素存放在下标1~n:。
A*:
当题目一定有解时才用,无解时效率低于普通BFS。
边权任意,但不能有负权回路。
将BFS中的队列换成优先队列(小根堆)。队列中存储从起点到当前点的真实距离与从当前点到终点的估计距离之和。
Dijkstra可以看成是特殊的A*算法(所有估计距离为0)
设当前状态为state,从起点到当前点的真实距离为d(state),从当前点到终点的估计距离为f(state),从当前点到终点的估计距离为g(state),则该算法的满足条件为:
当终点第一次出队时break,此时得到的就是最优解。
但不能保证除终点外其他点第一次出队时得到的是最小值。
//算法正确性证明(反证法):
若终点第一次出队时得到的距离dist不是最优解,在最优路径上任取一点u,必有:
则。与优先队列(小根堆)性质矛盾,dist不可能在d(u)+f(u)之前出队,可知假设不成立。那么终点第一次出队时得到的距离dist一定是最优解。
AcWing 178. 第K短路
//当终点第k次出队时break,此时得到的就是第K短路。
可用数学归纳法证明:与上面类似地,若终点第k次出队时得到的距离dist不是第K短路,在第K短路上任取一点u,必有:
则
与优先队列(小根堆)性质矛盾,dist不可能在d(u)+f(u)之前出队,可知假设不成立。那么终点第k次出队时得到的距离dist一定是第K短路。
AcWing 179. 八数码
//八数码问题有解的充分必要条件:
读入的数字串的逆序对的数量是偶数。
数字的左右移动不会改变数字串的逆序对的数量,上下移动只是将某一数字移动到另两个数字之后,逆序对改变的数量为2,不影响数字串的逆序对的数量的奇偶性。
八数码问题的估价函数:当前状态中每个数与它的目标位置的曼哈顿距离之和。
- 内部搜索:内部的某一部分能否走到另一部分,不回溯
- 外部搜索:将整体当成一个点,在整体与整体之间搜索,要回溯
DFS之连通性模型:
- Flood fill
- 图与树的遍历
AcWing 1112. 迷宫
AcWing 1113. 红与黑
DFS之搜索顺序:
AcWing 1116. 马走日
AcWing 1117. 单词接龙
AcWing 1118. 分成互质组
//按照组合方式搜索,而不是按照排列方式搜索,这样会少很多分支。避免搜过123之后又搜132
1.把某个数加到最后一组
2.新开一个组
只有当所有的数都不能加到最后一组时,才新开一个组。
DFS常见剪枝方法:
先考虑搜索顺序,再考虑优化方法
如何进行剪枝操作须根据具体情况具体分析。
- 优化搜索顺序
大部分情况下,我们应该优先搜索分支较少的节点。
- 排除等效冗杂
- 可行性剪枝
- 最优性剪枝
如果当前递归得到的答案已经大于最优值就直接return
- 记忆化搜索(DP)
AcWing 165. 小猫爬山
//将重量从大到小排序:
AcWing 166. 数独
//剪枝:
- 用&来计算行、列、九宫格的交集,时间复杂度O(1)
- 用lowbit位运算优化来枚举可选数字(有几个1就循环几次)
AcWing 167. 木棒
//从小到大枚举木棒长度,从前往后依次拼木棒
木棍:题目给出的被砍断的
木棒:由若干个木棍拼成
//剪枝:
- 枚举sum的约数
- 优化搜索顺序:从小到大枚举
- 排除等效冗杂:
1) 按照组合数方式枚举
2) 如果当前木棍加到当前棒中失败了,则直接略过后面所有长度相等的木棍。
3) 如果是木棒的第一根木棍失败了,则搜索该木棒一定失败。
4) 如果是木棒的最后一根木棍失败了,则搜索该木棒一定失败。
AcWing 168. 生日蛋糕
//剪枝:
- 自底向上搜,从大到小先枚举R,再枚举H
- 预处理minv(u)和mins(u),可行性剪枝:v+minv(u)≤n,最优性剪枝:s+mins(u)﹤n
//max和min函数中的两个变量类型要一致
迭代加深:
每次搜索都有一个层数上限:max_depth
适用于某些分支层数很深,但答案在较浅层的情况。
AcWing 170. 加成序列
//剪枝:
- 优先枚举较大的数
- 排除等效冗余:用bool数组判断是否枚举过,避免出现例如1+4=2+3重复的情况
双向DFS:
与双向BFS类似,极大地提高了搜索效率。
AcWing 171. 送礼物
//用空间换时间,优先搜索较大的数。
//剪枝:
- 将所有物品按重量从大到小排序
- 先将前k件物品能凑出的所有重量打表,然后排序并判重
- 搜索剩下的n-k件物品的选择方式,然后在表中二分出不超过w的最大值
//把一个vector去重:
把一个数组去重,元素存放在下标1~n:
IDA*:
配合迭代加深,更简单、实用。
预估当前状态到目标状态所需的步数,若已超过步数上限则无需继续搜索(剪枝)。
满足条件(同A*算法):估价函数≤真实值
AcWing 180. 排书
//估价函数:
tot:错误的后继数量
如图所示,若将子串i移动到图示位置,则数列中后继发生改变的点共有三个(图中用蓝色标出)。
AcWing 181. 回转游戏
//估价函数:8-cnt
cnt:中间八个格子中出现次数最多的数字的出现次数
//此题说明了打表的便利和重要性!!!