ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

meet-in-the-middle 基础算法(优化dfs)

2019-10-13 17:52:14  阅读:343  来源: 互联网

标签:sta ++ tot middle 搜索 dfs meet


   $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[50],cntl,cntr,ans=-1;
bool vis[1<<22];
inline int read(){
    re int a=0,b=1; re char ch=getchar();
    while(ch<'0'||ch>'9')
        b=(ch=='-')?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9')
        a=(a<<3)+(a<<1)+(ch^48),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[1<<22],r[1<<22];
inline void dfs1(re int x,re int tot,re int sta){
    if(x>(n>>1)){
        l[++cntl]=node(sta,tot);
        return ;
    }
    dfs1(x+1,tot,sta);
    dfs1(x+1,tot+a[x],sta|(1<<(x-1)));
    dfs1(x+1,tot-a[x],sta|(1<<(x-1)));
}
inline void dfs2(re int x,re int tot,re int sta){
    if(x>n){
        r[++cntr]=node(sta,tot);
        return ;
    }
    dfs2(x+1,tot,sta);
    dfs2(x+1,tot+a[x],sta|(1<<(x-1)));
    dfs2(x+1,tot-a[x],sta|(1<<(x-1)));
}
signed main(){
    n=read();
    for(re int i=1;i<=n;++i) a[i]=read(); 
    dfs1(1,0,0); dfs2((n>>1)+1,0,0);
    sort(l+1,l+cntl+1,less<node>());
    sort(r+1,r+cntr+1,greater<node>());
    for(re int i=1,j=1,pos;i<=cntl&&j<=cntr;++i){
        while(j<=cntr&&l[i].val+r[j].val>0)  ++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]=1,++ans;    
        }
    }
    printf("%d\n",ans);
}
Code

  

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

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

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

标签:sta,++,tot,middle,搜索,dfs,meet
来源: https://www.cnblogs.com/Hzoi-lyl/p/11667349.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有