$meet-in-the-middle$(又称折半搜索、双向搜索)对于$n<=40$的搜索类型题目,一般都可以采用该算法进行优化,很稳很暴力。

  $meet-in-the-middle$算法的主要思想是将搜索区域化为两个集合,分别由搜索树的两端向中间扩展,直到搜索树产生交集,此时即可得到我们的合法情况。

  通常适用于求经过$n$步变化,从A集合变到B集合需要的方案数问题。

  对于普通dfs来说,其一大弊端是随着搜索层数的不断增加,搜索的复杂度也会极速增长,

  而$meet-in-the-middle$算法能够将搜索层数降至原来的一半,对整体效率而言无疑是不小的提升。

  如图所示:

      

  可以明显看出$meet-in-the-middle$对于dfs优化之显著。

  那么考虑$meet-in-the-middle$的算法流程:

    1、从状态$L$出发,经过$x$步状态扩展,记录到达状态$i$的步数,通常这里会与状压、二分等内容结合。

    2、从状态$R$出发,经过$y$步状态扩展,若同样到达状态$i$并且步数不为0,则累加答案。

  通常我们需要保证$x+y=n$,也就是需要满足状态总数不变,只是深度减少,一般情况下取$x=y=(n>>1)$最优

  但具体情况应视题而定,可能搜索深度不均匀时,效率反而会更高。

  

  下面来看一道例题:

  [BZOJ 2679] Balanced Cow Subsets

  简要题意:有多少个非空子集,能划分成和相等的两份。$(n<=20)$

  分析:显然的$meet-in-the-middle$定义,考虑如何转化

  将题设转化成方程的形式$a1x1+a2x2+a3x3+...+anxn=0$,其中$x=0,1,-1$(表示不选,选入集合$l$,选入集合$r$),那么移项可得一种两侧各有一种集合的形式,根据题目要求,我们需要求出可以构建出该方程的集合方案数,可以采用状压的思想,记录所选取的数的和以及选取集合的状态即可。

  

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define re register
using namespace std;
int n,a[],cntl,cntr,ans=-;
bool vis[<<];
inline int read(){
re int a=,b=; re char ch=getchar();
while(ch<''||ch>'')
b=(ch=='-')?-:,ch=getchar();
while(ch>=''&&ch<='')
a=(a<<)+(a<<)+(ch^),ch=getchar();
return a*b;
}
struct node{
int sta,val; node(){}
node(int x,int y){sta=x,val=y;}
bool operator < (const node &b) const {
return val<b.val;
}
bool operator > (const node &b) const {
return val>b.val;
}
}l[<<],r[<<];
inline void dfs1(re int x,re int tot,re int sta){
if(x>(n>>)){
l[++cntl]=node(sta,tot);
return ;
}
dfs1(x+,tot,sta);
dfs1(x+,tot+a[x],sta|(<<(x-)));
dfs1(x+,tot-a[x],sta|(<<(x-)));
}
inline void dfs2(re int x,re int tot,re int sta){
if(x>n){
r[++cntr]=node(sta,tot);
return ;
}
dfs2(x+,tot,sta);
dfs2(x+,tot+a[x],sta|(<<(x-)));
dfs2(x+,tot-a[x],sta|(<<(x-)));
}
signed main(){
n=read();
for(re int i=;i<=n;++i) a[i]=read();
dfs1(,,); dfs2((n>>)+,,);
sort(l+,l+cntl+,less<node>());
sort(r+,r+cntr+,greater<node>());
for(re int i=,j=,pos;i<=cntl&&j<=cntr;++i){
while(j<=cntr&&l[i].val+r[j].val>) ++j;
for(pos=j;l[i].val==-r[pos].val;++pos){
re int sta=(l[i].sta|r[pos].sta);
if(!vis[sta]) vis[sta]=,++ans;
}
}
printf("%d\n",ans);
}

Code

  

   同种类的题目还有许多,例如  [POJ 1186] 方程的解数,  [BZOJ 4800] 冰球世界锦标赛  等。

    综上可见$meet-in-the-middle$算法的应用之广。

    至此,通过分析转化搜索模型,达到了降低搜索复杂度,优化程序效率的目的。

meet-in-the-middle 基础算法(优化dfs)的更多相关文章

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

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

  2. Meet in the middle算法总结 (附模板及SPOJ ABCDEF、BZOJ4800、POJ 1186、BZOJ 2679 题解)

    目录 Meet in the Middle 总结 1.算法模型 1.1 Meet in the Middle算法的适用范围 1.2Meet in the Middle的基本思想 1.3Meet in ...

  3. 0基础算法基础学算法 第八弹 递归进阶,dfs第一讲

    最近很有一段时间没有更新了,主要是因为我要去参加一个重要的考试----小升初!作为一个武汉的兢兢业业的小学生当然要去试一试我们那里最好的几个学校的考试了,总之因为很多的原因放了好久的鸽子,不过从今天开 ...

  4. 折半搜索(meet in the middle)

    折半搜索(meet in the middle) ​ 我们经常会遇见一些暴力枚举的题目,但是由于时间复杂度太过庞大不得不放弃. ​ 由于子树分支是指数性增长,所以我们考虑将其折半优化; 前言 ​ 这个 ...

  5. Meet in the middle

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

  6. 浅谈Meet in the middle——MITM

    目测观看人数 \(0+0+0=0\) \(\mathrm{Meet\;in\;the\;middle}\)(简称 \(\rm MITM\)),顾名思义就是在中间相遇. 可以理解为就是起点跑搜索树基本一 ...

  7. ACM基础算法入门及题目列表

    对于刚进入大学的计算机类同学来说,算法与程序设计竞赛算是不错的选择,因为我们每天都在解决问题,锻炼着解决问题的能力. 这里以TZOJ题目为例,如果为其他平台题目我会标注出来,同时我的主页也欢迎大家去访 ...

  8. DAY 4 基础算法

    基础算法 本来今天是要讲枚举暴力还有什么的,没想到老师就说句那种题目就猪国杀,还说只是难打,不是难.... STL(一)set 感觉今天讲了好多,set,单调栈,单调队列,单调栈和单调队列保证了序列的 ...

  9. 【算法】342- JavaScript常用基础算法

    一个算法只是一个把确定的数据结构的输入转化为一个确定的数据结构的输出的function.算法内在的逻辑决定了如何转换. 基础算法 一.排序 1.冒泡排序 //冒泡排序function bubbleSo ...

  10. SQL Server 聚合函数算法优化技巧

    Sql server聚合函数在实际工作中应对各种需求使用的还是很广泛的,对于聚合函数的优化自然也就成为了一个重点,一个程序优化的好不好直接决定了这个程序的声明周期.Sql server聚合函数对一组值 ...

随机推荐

  1. atom的使用

    一,Atom介绍 Atom 是 Github 开源的文本编辑器,这个编辑器完全是使用Web技术构建的(基于Node-Webkit).启动速度快,提供很多常用功能的插件和主题,可以说Atom已经足以胜任 ...

  2. 全面理解python中self的用法

    self代表类的实例,而非类. class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt() 执行结果 ...

  3. C++ 系列:extern

    extern 作用1:声明外部变量现代编译器一般采用按文件编译的方式,因此在编译时,各个文件中定义的全局变量是互相透明的,也就是说,在编译时,全局变量的可见域限制在文件内部. 例1:创建一个工程,里面 ...

  4. 「题解」:windy数

    问题: windy数 时间限制: 1 Sec  内存限制: 512 MB 题面 题目描述 Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 的正整数被称为 Windy 数. ...

  5. iOS开发CoreData的多表关联

    1.多表关联 多表关联,对SQL 数据库的操作,在一张表的数据中可以引用另外一张表里的数据.通过 Entity 实体中的 Relationships 来实现,比起传统的 SQL 数据库来,更加简单. ...

  6. 安装 adb centos 7

    打开 https://centos.pkgs.org/7/epel-x86_64/android-tools-20130123git98d0789-5.el7.x86_64.rpm.html 下载 r ...

  7. Linux查看温度

    step 1: centos $ sudo yum install lm_sensors ubuntu $ sudo apt-get install lm_sensors step2$ sudo se ...

  8. python基础-迭代器

    1.迭代:指的是一个重复的过程,每一次重复称为一次迭代,并且每一次重复的结果 是下一次重复的初始值 2.为什么要有迭代器 对于序列类型:str list tuple 可以依赖索引来迭代取值,但是对于d ...

  9. 排列组合lucas模板

    //codeforces 559C|51nod1486 Gerald and Giant Chess(组合数学+逆元) #include <bits/stdc++.h> using nam ...

  10. 【error】vue-style-loader didn't discriminate between server and client

    出现这个bug的时候,设置为false