$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. 输出内容 document.write() 可用于直接向 HTML 输出流写内容。简单的说就是直接在网页中输出内容

    输出内容(document.write) document.write() 可用于直接向 HTML 输出流写内容.简单的说就是直接在网页中输出内容. 第一种:输出内容用""括起,直 ...

  2. C++:多线程001

    C++ 多线程 创建线程的API函数 HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD:线程安全相关的属性,常置为N ...

  3. Powerdesigner 生成数据字典

    https://www.jianshu.com/p/f491d0d3c503http://blog.csdn.net/adparking/article/details/50402980http:// ...

  4. 廖雪峰Java13网络编程-3其他-2RMI远程调用

    1.RMI远程调用: Remote Method Invocation 目的:把一个接口方法暴露给远程 示例: 定义一个接口Clock,它有一个方法能够获取当前的时间,并编写一个实现类,来实现这个接口 ...

  5. 异或前缀和,组合数学——cf1054D

    /* 每个异或前缀和sum[i]只有两个值 区间异或和不为0,即两个不相等的前缀和 sum[i]的两个前缀和只要标记一个就可以了,为了去重只用map保存最小的那个来计数 最后统计相同的前缀和时,为了使 ...

  6. Spring boot配置Dubbo三种方式

    方式一 使用注解的方式 导入dubbo-starter 在application.properties配置属性 使用@Service暴露服务 使用@Reference引用服务 使用@EnableDub ...

  7. IT技术博客

    博客收藏大全: 陈皓博客: 陈硕的博客: 风云的博客: 当然我在扯淡: hellogirl: 田守枝Java技术博客: 廖雪峰博客: Milo游戏开发:

  8. Delphi XE10百集视频教程计划

    1. 前言 本人现在的职业是Java程序员,一直想学习一个做桌面应用的编程语言,几年前无意中接触到Delphi,比VB功能强大,比C++语法更容易理解,加上Oracle的PL/SQL的底子,最终决定学 ...

  9. Delphi遍历进程-Win32API

    本博客的Delphi代码使用的版本均为DelphiXE10.x 1.1 .枚举进程 通过进程名称获取指定的进程ID,代码很详细,不再赘述 unit Uuitls; interface uses TlH ...

  10. linux与window文件传输(使用ssh+putty)

    linux与window文件传输(使用ssh+putty) https://blog.csdn.net/Imagine_Dragon/article/details/78303241