$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. 洛谷P4514 上帝造题的七分钟

    P4514 上帝造题的七分钟 题目背景 裸体就意味着身体. 题目描述 "第一分钟,X说,要有矩阵,于是便有了一个里面写满了000的n×mn×mn×m矩阵. 第二分钟,L说,要能修改,于是便有 ...

  2. 单体内置对象——Global对象

    单体内置对象的定义:由ECMAScript实现提供的.不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前已经存在了.意思就是说:开发人员不必显式地实例化内置对象,因为他们已经实例化了. ...

  3. python spark环境配置

              在配置Hadoop之前,应该先做以下配置   1.更改主机名 首先更改主机名,目的是为了方便管理.  输入:hostname  查看本机的名称  使用 hostname 修改当前主 ...

  4. odoo 下 get_object_reference 函数

    get_object_reference是 ir.model.data 模块中下的一个函数 该函数通过调用ir.model.data 模块中另外一个函数 xmlid_lookup 返回结果 def g ...

  5. 【JZOJ3317】管道

    description Hotham市再次被Jester袭击.这一次,Jester攻击的目标是Hotham的供水系统.Hotham的淡水存储在N个水库,由M个管道连接. 任意2个水库之间至少有一条路径 ...

  6. python下使用ElasticSearch

    一 什么是 ElasticSearch Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elas ...

  7. 2019/10/17 CSP模拟 总结

    T1 补票 Ticket 没什么好说的,不讲了 T2 删数字 Number 很后悔的是其实考场上不仅想出了正解的方程,甚至连优化都想到了,却因为码力不足只打了\(O(n^2)\)暴力,甚至还因为细节挂 ...

  8. [洛谷P2472] [SCOI2007]蜥蜴

    题目链接: 蜥蜴 题目分析: 一道网络流,先来分析一下问题: 在一个\(r*c\)的图中分布了一些数,其他地方都用\(0\)填充,我们分别从指定的一些数出发,每次可以移动到周围距离为\(d\)以内的数 ...

  9. JDK1.8 之Lambda表达式

    概述 Lambda 表达式是一种匿名函数(对 Java 而言这并不完全正确,但现在姑且这么认为),简单地说,它是没有声明的方法,也即没有访问修饰符.返回值声明和名字. 你可以将其想做一种速记,在你需要 ...

  10. MySQL-Utilities:mysqldbcompare及跳过复制错误

    mysqldbcompare也是MySQL-Utilities工具集的一个脚本.mysqldbcompare从两个数据库比较对象和数据的不同.数据库中的对象包括:表.视图.触发器.存储过程.函数和事件 ...