这周主要练习了搜索算法,整理一下本周的收获。
目前遇到的搜索问题,大致可以划分为以下几类:
1.填数/填表
一般是给定一个规则,要求你按这个规则去找出问题的可行解。常见的问题如全排列,八皇后,数独等。这种题一般用dfs解决,因为这种问题后一个状态完全取决于前一个状态,dfs搜到的一条合法路径即为问题的解。但是原始的dfs必须再搜索完整个过程再判断是否合法显然太慢了,因为可能搜索过程中已经发现不合理了,就可以直接返回,从而加快搜索过程。而且该问题可以存在多解,此时可以通过回溯,返回上一步尝试其他的路线是否可行。
P1219 [USACO1.5]八皇后 Checker Challenge - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P2404 自然数的拆分问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
2.方案选择求最优解
比较经典的例题就是搜索解法的01背包问题。对于一个物品我们可以选择,也可以不选择,每个选择都会影响之后的结果,要得到最优解,那么最简单的方法就是搜索所有的可能性,把每种物品选不选都考虑一遍,最后把所有可能的结果取最值即可得到最优解。同样,常用dfs回溯实现,过程中也可以判断一些不合理的情况进行剪枝。但是这样的时间复杂度仍然过大,所以我们可以采用记忆化的方式,记录每一种经历过的状态,避免重复计算提高效率
这种题使用搜索算法的好处就是易于输出最优解的路径——每次更新最优解的时候利用判断数组来拷贝一份更新路径数组,或者用记忆化搜索的时候利用链式数组来记录最优子结构的路径
如:
[USACO2.1]健康的荷斯坦奶牛 Healthy Holsteins - 洛谷
常规的只需要输出最优解的题:
P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
3.迷宫类
大致总结就是给一个无权图,每步可以选择一种走法,问到终点的最短步数。题目中可能加上障碍物(各种形式)使某些区域不能到达。
这种题一般常用bfs,因为bfs第一次搜到的路径就是最短路径。每次推入状态的时候要判断该状态是否合法(能不能走,是否越界等)。而且当下一状态就是目标点的时候可以直接结束搜索,不用再推入状态遍历队列了。
常规的题如:
P1443 马的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1605 迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
也有更复杂的,障碍会随时间变化的,但是思路本质上也一样,但是需要预处理一下图
[USACO08FEB]Meteor Shower S - 洛谷
4.连通块问题
也算是比较常见的搜索问题了,初见的时候可能无从下手,但是细想其实就是遍历图,找到连通块可疑点,然后用bfs标记这块连通块地图数组,将其于正常的位置区分。然后继续遍历,因为连通块整个被bfs标记了,遍历到连通块的所属点时就可以判断出此处是连通块还是正常位置。然后按照题目需求输出连通块的数量等等。
这块问题遇到的不多,但都大同小异
[USACO10OCT]Lake Counting S - 洛谷
最后,搜索本质上还是一种暴力算法,时间复杂度一般偏高,但有点在于好理解,而且代码简洁
如dfs的一般形式为:
dfs(){
if(搜索结束)
判断是否合法;
else
搜索
回溯
搜索其他状态
...
}
bfs一般形式为
while(搜索未结束){
得到前一状态now
枚举now的所有可行的子状态
if(该状态满足目标条件)
搜索结束
else
推入该状态进队列继续搜索
}
有时一些正解不是搜索的题目也可以写一发暴力搜索得到初步的答案,从而得到规律,思考正确做法。