折半搜索(meet in middle)

发布时间 2023-07-15 20:25:05作者: 为么要取名字

折半搜索

做法为将整个搜索的过程分为两部分,然后每部分分别进行搜索,最后将得到两个答案序列,再将答案序列进行合并,即可得到最终的答案。

可以发现,当状态非常之多的时候,这种优化还是非常明显的,最优情况下可以直接把复杂度开个根号。

需要注意的是,折半搜索应用的时候需要满足以下条件:

  • 搜索各项不能相互干扰。

  • 需要满足搜索状态可逆,就是说一个状态可以从两个方向都得到。

折半搜索其实还是用的比较广泛的。

BFS ,DFS 还有状压 DP 都有类似的应用。

折半搜索一般的难点就在于最后的答案序列合并。(可能会使用一些奇奇怪怪的高深的玩意才能搞得出来)

实现非常灵活,需要按照题目来进行选择。

一般比较常见的排序后二分,双指针还有哈希表。

逐个分析一下:

1.排序后二分

复杂度肯定是带 log⁡ 的。为什么正确?

在一个有序的序列中,如果我们可以找到一个位置可以做到对答案有贡献,那么这个位置之前的所有位置都是可以对答案有贡献的,所以直接统计就好。

1 sort(a+1,a+1+cnta);
2 for(re int i=1;i<=cntb;i++)
3     ans+=upper_bound(a+1,a+1+cnta,m-b[i])-a-1;

2.双指针

一般是线性的,效果很不错,代码比较好写。

缺点就是比较难想,需要考虑一些问题(例如单调性)。

1 int l=cnta,r=1;
2 for(r=1;r<=cntb;r++){
3     while(a[l]+b[r]>m)l--;//m是一个限制条件
4     if(a[l]&&b[r])ans+=(l-1);  
5 } 

3.哈希表

也是线性,至于具体如何就要看脸了。

如果不被卡的话哈希表确实是一种非常不错的选择。

具体就是先处理一半,然后把搜到的答案存到哈希表里,然后搜另一半,之后再去哈希表里找,把结果合并就可以了。

 1 void dfs1(){//搜索一半 
 2     if (到达边界){
 3         add(hash(x)); 
 4         return;
 5     } 
 6     ... 
 7 }
 8 
 9 void dfs2(){//处理另一半 
10     if (到达边界){
11         ans+=sum[hash(x)];
12         return;
13     } 
14     ... 
15 }

例题:

P5691 [NOI2001] 方程的解数

P4799 [CEOI2015 Day2]世界冰球锦标赛

P3067 [USACO12OPEN]Balanced Cow Subsets G

CF888E Maximum Subsequence

 摘自洛谷博客

    https://www.luogu.com.cn/blog/LawrenceSivan/p5691-noi2001-fang-cheng-di-xie-shuo-zhe-ban-sou-suo-post