搜索优化
主要有:剪枝、双向搜索、启发式搜索、迭代加深。
【剪枝】
一道水题。基本想法:枚举每根木棍属于哪条边,
但是这太慢了,我们剪枝:如果一条边目前的累计长度
于是我们就通过了。
先可以暴力枚举每个男匹配哪个女。
然后我们考虑优化:
如果对于两个匹配:
若
若
【双端队列广搜】
想法:
对于每一个方格,我们把它的两组对角顶点连边。若一组顶点本来就被电线连接,其边权为 0;否则边权为 1。
发现此题边权只有两种,我们引入一种新的广搜:双端队列广搜。
考虑我们原本广搜求最短路的条件:无边权。(一种边权)
这是因为我们需要保证当第一次搜索到一个点的时候,当前距离一定刚好是最短路。
而现在我们可以用双端队列,保证我们一定是先搜完 0 的点、再搜完 1 的点。
具体而言,当我们遇到一个 0 的点,把它从队头入队;否则从队尾入队。
这样我们的搜索过程就像先把 0 的点给 floodfill 了一样。
0 的点在我们搜到的时候一定是最短路,而 1 的点一定在我们搜完所有 0 的点才会到,因此我们搜到的点第一次一定是最短的。
【双向广搜】
如果我们暴力枚举每一个元素取或不取,
但是,我们可以考虑把序列分成两半:先求出前
具体而言,我们先
注(法二):
我们也可以把两次搜索的和们分别存进两个数组里面,然后用双指针扫一遍求最小距离。
这是一道经典而简单的问题,但是我们要用双向广搜来写。
我们可以考虑建立两个队列,一个代表从起点搜,一个代表从终点搜。另外我们也建立两个
当我们搜索的时候,每次都同时搜出起点和终点的下一层。并且当我们在搜索起点的时候,如果遇到一个终点搜过的格子,我们就可以把这个格子到起点、终点的最短路长度加起来求出从起点到终点的最短路长度。
正确性是显然的。因为无论是起点还是终点,我们都在做正常的广搜,所以每一个格子被搜的时候其最短路长度已经确定,并且我们的层数是从小到大搜的。
因此,当我们起点碰到终点的时候,我们可以确定这个格子以后不会出现更短的路径。
注意处理 起点=终点 的特殊情况。
双向广搜为什么能加快速度?
我们做正常广搜的时候,相当于在起点画圆,不断扩大半径,直到终点被包括进来。
双向广搜时,我们相当于同时在起点和终点画圆,同时扩大半径,直到两个圆有交点。
因为我们从起点和终点一起搜,所以圆的半径减半,那么 面积就会变成
虽然题目中可能并没有画圆一样整齐的扩张,但是双向搜索仍能减少复杂度。
【启发式搜索】
启发式:任何不影响正确性的优化,都可以叫做启发式。
【启发式剪枝】
最初想法:
枚举原始木棍长度,全排列枚举,参数里面记录当前木棍的剩余长度。
优化:
-
原始木棍长一定是总长的因数;
-
避免同组内的木棍又排列一遍重复,令选择时必须比组内上一根木棍短;
-
组间重复,令每个组的第一根木棍长度递减;
-
重复数,如果选了当前木棍不行,就不要选和当前木棍同长度的木棍了,这可以用 cnt 数组完成,每次搜索循环所有长度,如果还有该长度木棍就尝试,不行直接循环下一种长度;
-
如果当前剩余的长度可以刚好和一根木棍匹配,一定要用这个木棍,如果用了这个木棍后返回不能拼成,一定是之前的木棍选错了,这是因为如果不选这个木棍,一定需要用几根更小的拼出来,不如直接用这个木棍;
-
如果当前木棍当第一根时不行,一定是之前选错了。
加上这些优化,就比较快了。
【A*】
对于一个人,我们在枚举答案的时候一定会想要从那些 “看起来更可能是答案” 的方向枚举。
但是计算机并不理解我们的想法,于是我们现在要尝试让计算机也理解我们的想法。
以找棋盘最短路为例。
我们可以建立一个估值函数
我们令
比如,如果我们把
上面我们说明了启发式搜索可以让我们的搜索更快,下面我们看看如何保证正确性。
正确性:
实际上,启发式已经破坏了广搜的正确性。
因为我们广搜的正确性是因为:当我们搜索
但是,我们启发式可能朝靠近
假如我们能保证
而我们会优先朝靠近
从靠近
而上面又有
注意
而这个最短距离已经被证实比其他不走这个方向的最短距离 更短,所以这个距离就是真正的最短距离。
综上所述,只要满足
还要注意一种特殊情况:
? | 5 | ... |
---|---|---|
3 | 4 | 5 |
... | ... | ... |
如图,我们现在搜到了 "3" 的格子,那么 “?” 的最短距离应当是 4,但是有可能因为 ? 的
但是我们不需要担心我们会用这个错误的距离更新终点导致答案错误。
-
如果 ? 就是终点,则
,而我们上面没有第一时间取出 ?的原因是因为 更差,矛盾; -
如果 ? 用来更新终点,那么对于我们在 3 更新时加入的 ? 状态,它的
,而如果我们用了 的状态去更新终点,它比 还要大,那么我们肯定在这之前就已经用了 的状态更新过一遍了。
我们尝试推广第二种情况:
假如我们有两条去终点的路径,但是用了更长的那一条去更新了终点。
在更短的那一条路上取一个没有在另一条路径上的点
而
所以
这题也可以用 A* 做。我们定义
【迭代加深】
众所周知,深搜的特点是一直往底走,走到底了再回头。
但是,如果我们遇到了一道没有底的题,也就是结束条件模糊的题,我们应该怎么用深搜做?
这个时候,我们就可以用迭代加深搜索。
我们可以人为限定一个最大深度,如果深搜的深度大于它,我们就认为在这个深度下,我们这么走是没有答案的,然后回溯。
每次将限定的深度增大一点,直到在限定深度内搜到答案。因为搜索树大小是指数级增长,所以之前搜索的时间可以当做一个较大的常数来看。
这题的特点在于我们不知道一共需要几个分数加起来,导致我们可能会搞出一大堆分母特别大的分数,然后无限往下搜。
于是我们循环需要的分数个数搜索。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战