Meet in the Middle学习笔记

Meet in the Middle算法

Meet in the Middle算法可以看成是搜索算法的一个改进,一般来说用于广搜(BFS),不过如果搜索深度有上限的情况下也可以用深搜。

我们首先假象一个搜索场景
这里写图片描述

假设从上面的红点开始进行搜索,找一条能通向下面那个红点的路径,每个点都有两条岔路可供选择。

显然如果我们简单的从上面那个点开始BFS,代价是较大的,在最差的情况下,可能需要把整张图的45个节点给走一遍。

当然把搜索的重任全部交给一个节点是不合理的。于是Meet in the Middle算法就诞生了。顾名思义,就是两个节点各自向中间搜索,直至碰头。即双向搜索
这里写图片描述

在上图中红点的部分为起点开始向外搜索到的点,而蓝点表示从终点开始搜索到的点。假如此时搜索进行到点B,直接就能发现A点是从起点过来的点,那么也就自然找到了一条从 起点->A->B->终点 的路径,然而我们发现,整个图里面黑色部分的节点与边其实我们都可以不用访问。当整张图扩大是,这种优化的效果还是比较明显的。

我们接下来用数学来具体推导一下这种搜索方案的复杂度。

假设向外搜索n层需要的代价为f(n)。如果不用meet in the middle 那么复杂度当然是O(f(n))。

如果使用了meet in the middle,那么从起点开始需要搜索的代价为f(n/2),从终点开始搜索的代价也是f(n/2),总代价就是2*f(n/2),复杂度为O(f(n/2))。

当然从这个搜索模型来看,无论是不是使用meet in the middle复杂度均为O(n^2)。但是当f(n)为指数函数时该算法的效果就会非常明显。比如f(n)=2^n,那么不用meet in the middle复杂度即为O(2^n),但是使用了meet in the middle的复杂的只有O(2^(n/2)),相当于直接开了一个根号。meet in the middle算法也有一定的局限性,首先是要求搜索图中的节点状态都是可逆的,即如果A点能到B点,B点也能一下子回到A点,否则就不存在从终点出发这个说法了。

其次是需要额外的数据结构存储找过的节点,一般来说空间足够都采用哈希表。

一般来说,meet in middle基于指数枚举看看数据范围,一般在30左右,即30/2=15,就可以2^15枚举了

题目

NOI2001方程的解数

题解

练一下meet in the middle
暴力搜索:150 ^ 6 =GG
meet in the middle :150 ^3 =能过

计算满足 a+b+c+d+e+f=0 的数的个数

可以算 (a+b+c)+ (d+e+f) = 0

dfs两次,每次dfs一半

结果用双指针逼近法、乘法原理 O(n) 处理

代码

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=10;
const int M=3375005;
int mb,n,m,k[N],p[N],tmp1[M],tmp2[M],sum1,sum2;
int ksm(int a,int k)
{
    int ans=1;
    for (;k;k>>=1,a=a*a)
        if (k&1) ans=ans*a;
    return ans;
}
void dfs(int t,int tot,int *tmp,int &sum)
{
    if (t>mb) {tmp[++sum]=tot;return;}
    for (int i=1;i<=m;i++) dfs(t+1,tot+k[t]*ksm(i,p[t]),tmp,sum);
}
int work()
{
    int ans=0;
    sort(tmp1+1,tmp1+sum1+1);
    sort(tmp2+1,tmp2+sum2+1);
    int l=1,r=sum2;
    for (;l<=sum1 && r>1;l++)
    {
        while (tmp1[l]+tmp2[r]>0) r--;
        int ll=1,rr=0;
        for (int j=r;tmp1[l]+tmp2[j]==0 && j>0;j--) rr++;
        while (l<sum1 && tmp1[l]==tmp1[l+1]) ll++,l++;
        ans+=ll*rr;
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d%d",&k[i],&p[i]);
    mb=n/2;
    dfs(1,0,tmp1,sum1);
    mb=n;
    dfs(n/2+1,0,tmp2,sum2);
    printf("%d",work());
}

部分引自这个up

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值