搜索是\(OI\)中一个十分基础也十分重要的部分,近年来搜索题目越来越少,逐渐淡出人们的视野。但一些对搜索的优化,例如\(A\)*,迭代加深依旧会不时出现。本文讨论另一种搜索——折半搜索\((meet\ in\ the\ middle)\)。

由一道例题引入:CEOI2015 Day2 世界冰球锦标赛

我们可以用以下代码解决\(n\leq 20\)的数据,时间复杂度\(O(2^n)\)

  1. void dfs(int step, int sum)
  2. {
  3. if (sum>m) return;
  4. if (step==n+1) {ans++; return;}
  5. dfs(step+1, sum+a[step]);
  6. dfs(step+1, sum);
  7. }

\(dfs\)有何弊端?

当搜索层数增加时,时间复杂度增加过快。

可不可以减少搜索层数,甚至降至一半?

当然可以。不然我这篇文章写什么

看网上两张很好的图就一目了然了。

于是我们从\(1\)和\(n\)搜索\(\frac{n}{2}\)的深度,然后得到两个长为\(2^{\frac{n}{2}}\)的序列,对于第一个排序,然后用第二个在第一个中二分查找并统计答案即可。

(此代码不开\(O2\)在洛谷会\(T\)一个点,在\(loj\)跑的飞快,可能是满屏\(vector\)的缘故。)

  1. #pragma GCC optimize (2)
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. #define int long long
  6. #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
  7. #define per(i, a, b) for (register int i=(a); i>=(b); --i)
  8. using namespace std;
  9. const int N=45;
  10. vector<int> a, b;
  11. int c[N], m, ans, n, mid;
  12. inline int read()
  13. {
  14. int x=0,f=1;char ch=getchar();
  15. for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
  16. for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
  17. return x*f;
  18. }
  19. void dfs1(int step, int now)
  20. {
  21. if (now>m) return;
  22. if (step>mid) {a.push_back(now); return;}
  23. dfs1(step+1, now+c[step]);
  24. dfs1(step+1, now);
  25. }
  26. void dfs2(int step, int now)
  27. {
  28. if (now>m) return;
  29. if (step>n) {b.push_back(now); return;}
  30. dfs2(step+1, now+c[step]);
  31. dfs2(step+1, now);
  32. }
  33. signed main()
  34. {
  35. n=read(); m=read(); mid=n+1>>1;
  36. rep(i, 1, n) c[i]=read();
  37. dfs1(1, 0); dfs2(mid+1, 0);
  38. sort(b.begin(), b.end());
  39. for (int i:a) ans+=upper_bound(b.begin(), b.end(), m-i)-b.begin();
  40. printf("%lld\n", ans);
  41. return 0;
  42. }

再来看另一道例题:USACO12OPEN 平衡的奶牛群

可以看看官方题解

有一种显然的暴力,子集枚举即可, 时间复杂度\(O(3^n)​\),无法通过。

我们把奶牛分为两组:黑色和白色。若\(S\)可行,那么\(S\)可被分为\(A,B\),使得\(sum_{A,black}-sum_{B,black}=sum_{B,white}-sum_{A,white}\)。于是我们可以计算黑色牛每一个子集可能的差值,白色同理。然后对于相同的差值进行配对,统计答案即可。

时间复杂度\(O(3^{\frac{n}{2}}\cdot 2^{\frac{n}{2}})\),即\(O((\sqrt{6})^n)\),可以通过。

依旧满屏\(vector\)

  1. #include<cstdio>
  2. #include<vector>
  3. #include<algorithm>
  4. #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
  5. #define per(i, a, b) for (register int i=(a); i>=(b); --i)
  6. using namespace std;
  7. inline int read()
  8. {
  9. int x=0,f=1;char ch=getchar();
  10. for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
  11. for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
  12. return x*f;
  13. }
  14. vector<pair<int, int> > solve(vector<int> S)
  15. {
  16. vector<pair<int, int> > ans;
  17. int n=S.size();
  18. rep(i, 0, (1<<n)-1)
  19. for (int j=i; ; j=(j-1)&i)
  20. {
  21. int sum=0;
  22. rep(k, 0, n-1)
  23. if (j&(1<<k)) sum-=S[k];
  24. else if (i&(1<<k)) sum+=S[k];
  25. if (sum>=0) ans.push_back(make_pair(sum, i));
  26. if (!j) break;
  27. }
  28. sort(ans.begin(), ans.end());
  29. ans.resize(unique(ans.begin(), ans.end())-ans.begin());
  30. return ans;
  31. }
  32. int main()
  33. {
  34. int n=read();
  35. vector<int> P, Q;
  36. rep(i, 0, n-1)
  37. {
  38. int x=read();
  39. if (i&1) P.push_back(x);
  40. else Q.push_back(x);
  41. }
  42. vector<pair<int, int> > L=solve(P), R=solve(Q);
  43. int p=0, q=0, l=L.size(), r=R.size();
  44. vector<bool> vis(1<<n);
  45. while (p<l && q<r)
  46. {
  47. if (L[p].first<R[q].first) p++;
  48. else if (L[p].first>R[q].first) q++;
  49. else
  50. {
  51. int p2=p, q2=q;
  52. while (p2<l && L[p2].first==L[p].first) p2++;
  53. while (q2<r && R[q2].first==R[q].first) q2++;
  54. rep(i, p, p2-1) rep(j, q, q2-1)
  55. vis[L[i].second|(R[j].second<<P.size())]=true,
  56. p=p2; q=q2;
  57. }
  58. }
  59. int ans=count(vis.begin()+1, vis.end(), true);
  60. printf("%d\n", ans);
  61. return 0;
  62. }

SP4580 ABCDEF

即\(a*b+c=d*(e+f),d\neq 0\)。先枚举前三个,后三个枚举后二分查找即可。

  1. #include<cstdio>
  2. #include<vector>
  3. #include<algorithm>
  4. #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
  5. #define per(i, a, b) for (register int i=(a); i>=(b); --i)
  6. using namespace std;
  7. vector<int> b, v, w;
  8. int a[105], n; long long ans;
  9. inline int read()
  10. {
  11. int x=0,f=1;char ch=getchar();
  12. for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
  13. for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
  14. return x*f;
  15. }
  16. void prep()
  17. {
  18. rep(i, 1, n) rep(j, 1, n) rep(k, 1, n)
  19. b.push_back(a[i]*a[j]+a[k]);
  20. sort(b.begin(), b.end());
  21. for (int i=0, j=0; i<b.size(); i=j+1, j++)
  22. {
  23. while (j<b.size()-1 && b[j+1]==b[i]) j++;
  24. v.push_back(b[i]); w.push_back(j-i+1);
  25. }
  26. }
  27. int check(int x)
  28. {
  29. int p=lower_bound(v.begin(), v.end(), x)-v.begin();
  30. if (v[p]==x) return w[p]; else return 0;
  31. }
  32. void calc()
  33. {
  34. rep(i, 1, n) rep(j, 1, n) rep(k, 1, n)
  35. if (a[i]) ans+=check((a[j]+a[k])*a[i]);
  36. }
  37. int main()
  38. {
  39. n=read();
  40. rep(i, 1, n) a[i]=read();
  41. prep(); calc();
  42. printf("%lld\n", ans);
  43. return 0;
  44. }

Meet in the middle的更多相关文章

  1. Meet in the middle学习笔记

    Meet in the middle(MITM) Tags:搜索 作业部落 评论地址 PPT中会讲的很详细 当搜索的各项互不影响(如共\(n\)个物品前\(n/2\)个物品选不选和后\(n/2\)个物 ...

  2. SPOJ4580 ABCDEF(meet in the middle)

    题意 题目链接 Sol 发现abcdef是互不相关的 那么meet in the middle一下.先算出abc的,再算def的 注意d = 0的时候不合法(害我wa了两发..) #include&l ...

  3. codevs1735 方程的解数(meet in the middle)

    题意 题目链接 Sol 把前一半放在左边,后一半放在右边 meet in the middle一波 统计答案的时候开始想的是hash,然而MLE了两个点 实际上只要排序之后双指针扫一遍就行了 #inc ...

  4. 【BZOJ4800】[Ceoi2015]Ice Hockey World Championship (meet in the middle)

    [BZOJ4800][Ceoi2015]Ice Hockey World Championship (meet in the middle) 题面 BZOJ 洛谷 题解 裸题吧,顺手写一下... #i ...

  5. 【CF888E】Maximum Subsequence(meet in the middle)

    [CF888E]Maximum Subsequence(meet in the middle) 题面 CF 洛谷 题解 把所有数分一下,然后\(meet\ in\ the\ middle\)做就好了. ...

  6. 【CF912E】Prime Game(meet in the middle)

    [CF912E]Prime Game(meet in the middle) 题面 CF 懒得翻译了. 题解 一眼题. \(meet\ in\ the\ middle\)分别爆算所有可行的两组质数,然 ...

  7. CF888E Maximum Subsequence (Meet in the middle,贪心)

    题目链接 Solution Meet in the middle. 考虑到 \(2^{35}\) 枚举会超时,于是分成两半枚举(尽量平均). 然后不能 \(n^2\) 去匹配,需要用到一点贪心: 将数 ...

  8. 【BZOJ4800】[Ceoi2015]Ice Hockey World Championship Meet in the Middle

    [BZOJ4800][Ceoi2015]Ice Hockey World Championship Description 有n个物品,m块钱,给定每个物品的价格,求买物品的方案数. Input 第一 ...

  9. cogs 304. [NOI2001] 方程的解数(meet in the middle)

    304. [NOI2001] 方程的解数 ★★☆   输入文件:equation1.in   输出文件:equation1.out   简单对比时间限制:3 s   内存限制:64 MB 问题描述 已 ...

随机推荐

  1. [C#]做服务使用Process启动外部程序没窗体

    这几天会到一个需要,要时时侦测文件生成,并上传到Server上,侦测文件生成使用的FileSystemWatch.但是时时运行遇到了问题,程序可能会人为退出或者意外终止,使用一个进程监控程序的监程,也 ...

  2. sql number类型和varchar2类型

    查询时,发现org_id 为number类型,zone_id为varchar2类型,需要转化 转换 to_char(),或者to_number select a.id,b.col,a.col from ...

  3. SpringMVC(前端设计模式)简介

    一.提供一个入口,让所有的请求都进行 / ,然后再分配给对应的页面,这就是前端设计模式(front) @WebServlet("/") 不过滤 .jsp public class ...

  4. Docker Compose demo 使用

    1.docker compose 安装 curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker- ...

  5. 微信小程序设置全局字体

    微信小程序设置全局css,需要在app.wxss文件中设置page的样式 page { font-family:"PingFangSC-Thin"; font-size:32rpx ...

  6. MySQL/Oracle视图的创建与使用

    1.什么是视图? 视图是一个虚拟的表,是一个表中的数据经过某种筛选后的显示方式,视图由一个预定义的查询select语句组成.   2.视图的特点. 视图中的数据并不属于视图本身,而是属于基本的表,对视 ...

  7. 更改Ubuntu默认python版本的两种方法python-> Anaconda

    当你安装 Debian Linux 时,安装过程有可能同时为你提供多个可用的 Python 版本,因此系统中会存在多个 Python 的可执行二进制文件.一般Ubuntu默认的Python版本都为2. ...

  8. ng-show和ng-if的区别和使用场景

    一.ng-show(ng-hide)和ng-if都是控制标签的显示和隐藏,为什么angularjs会定义两个指令来供我们使用呢,不多多说肯定有各自的使用场景,接下来我们看哈实际原理: ng-show实 ...

  9. hdu 5054

    http://acm.hdu.edu.cn/showproblem.php?pid=5054 确定是否矩形中点 这都能hack成功,无语 #include <cstdio> #includ ...

  10. js-倒计时原理

    <!DOCTYPE html><html>    <head>        <meta charset="UTF-8">      ...