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

由一道例题引入:CEOI2015 Day2 世界冰球锦标赛

我们可以用以下代码解决\(n\leq 20\)的数据,时间复杂度\(O(2^n)\)

void dfs(int step, int sum)
{
if (sum>m) return;
if (step==n+1) {ans++; return;}
dfs(step+1, sum+a[step]);
dfs(step+1, sum);
}

\(dfs\)有何弊端?

当搜索层数增加时,时间复杂度增加过快。

可不可以减少搜索层数,甚至降至一半?

当然可以。不然我这篇文章写什么

看网上两张很好的图就一目了然了。

于是我们从\(1\)和\(n\)搜索\(\frac{n}{2}\)的深度,然后得到两个长为\(2^{\frac{n}{2}}\)的序列,对于第一个排序,然后用第二个在第一个中二分查找并统计答案即可。

(此代码不开\(O2\)在洛谷会\(T\)一个点,在\(loj\)跑的飞快,可能是满屏\(vector\)的缘故。)

#pragma GCC optimize (2)
#include<cstdio>
#include<vector>
#include<algorithm>
#define int long long
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=45;
vector<int> a, b;
int c[N], m, ans, n, mid; inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
} void dfs1(int step, int now)
{
if (now>m) return;
if (step>mid) {a.push_back(now); return;}
dfs1(step+1, now+c[step]);
dfs1(step+1, now);
} void dfs2(int step, int now)
{
if (now>m) return;
if (step>n) {b.push_back(now); return;}
dfs2(step+1, now+c[step]);
dfs2(step+1, now);
} signed main()
{
n=read(); m=read(); mid=n+1>>1;
rep(i, 1, n) c[i]=read();
dfs1(1, 0); dfs2(mid+1, 0);
sort(b.begin(), b.end());
for (int i:a) ans+=upper_bound(b.begin(), b.end(), m-i)-b.begin();
printf("%lld\n", ans);
return 0;
}

再来看另一道例题:USACO12OPEN 平衡的奶牛群

可以看看官方题解

有一种显然的暴力,子集枚举即可, 时间复杂度\(O(3^n)​\),无法通过。

我们把奶牛分为两组:黑色和白色。若\(S\)可行,那么\(S\)可被分为\(A,B\),使得\(sum_{A,black}-sum_{B,black}=sum_{B,white}-sum_{A,white}\)。于是我们可以计算黑色牛每一个子集可能的差值,白色同理。然后对于相同的差值进行配对,统计答案即可。

时间复杂度\(O(3^{\frac{n}{2}}\cdot 2^{\frac{n}{2}})\),即\(O((\sqrt{6})^n)\),可以通过。

依旧满屏\(vector\)

#include<cstdio>
#include<vector>
#include<algorithm>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std; inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
} vector<pair<int, int> > solve(vector<int> S)
{
vector<pair<int, int> > ans;
int n=S.size();
rep(i, 0, (1<<n)-1)
for (int j=i; ; j=(j-1)&i)
{
int sum=0;
rep(k, 0, n-1)
if (j&(1<<k)) sum-=S[k];
else if (i&(1<<k)) sum+=S[k];
if (sum>=0) ans.push_back(make_pair(sum, i));
if (!j) break;
}
sort(ans.begin(), ans.end());
ans.resize(unique(ans.begin(), ans.end())-ans.begin());
return ans;
} int main()
{
int n=read();
vector<int> P, Q;
rep(i, 0, n-1)
{
int x=read();
if (i&1) P.push_back(x);
else Q.push_back(x);
}
vector<pair<int, int> > L=solve(P), R=solve(Q);
int p=0, q=0, l=L.size(), r=R.size();
vector<bool> vis(1<<n);
while (p<l && q<r)
{
if (L[p].first<R[q].first) p++;
else if (L[p].first>R[q].first) q++;
else
{
int p2=p, q2=q;
while (p2<l && L[p2].first==L[p].first) p2++;
while (q2<r && R[q2].first==R[q].first) q2++;
rep(i, p, p2-1) rep(j, q, q2-1)
vis[L[i].second|(R[j].second<<P.size())]=true,
p=p2; q=q2;
}
}
int ans=count(vis.begin()+1, vis.end(), true);
printf("%d\n", ans);
return 0;
}

SP4580 ABCDEF

即\(a*b+c=d*(e+f),d\neq 0\)。先枚举前三个,后三个枚举后二分查找即可。

#include<cstdio>
#include<vector>
#include<algorithm>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
vector<int> b, v, w;
int a[105], n; long long ans; inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
} void prep()
{
rep(i, 1, n) rep(j, 1, n) rep(k, 1, n)
b.push_back(a[i]*a[j]+a[k]);
sort(b.begin(), b.end());
for (int i=0, j=0; i<b.size(); i=j+1, j++)
{
while (j<b.size()-1 && b[j+1]==b[i]) j++;
v.push_back(b[i]); w.push_back(j-i+1);
}
} int check(int x)
{
int p=lower_bound(v.begin(), v.end(), x)-v.begin();
if (v[p]==x) return w[p]; else return 0;
} void calc()
{
rep(i, 1, n) rep(j, 1, n) rep(k, 1, n)
if (a[i]) ans+=check((a[j]+a[k])*a[i]);
} int main()
{
n=read();
rep(i, 1, n) a[i]=read();
prep(); calc();
printf("%lld\n", ans);
return 0;
}

Meet in the middle的更多相关文章

  1. Meet in the middle学习笔记

    Meet in the middle(MITM) Tags:搜索 作业部落 评论地址 PPT中会讲的很详细 当搜索的各项互不影响(如共\(n\)个物品前\(n/2\)个物品选不选和后\(n/2\)个物 ...

  2. SPOJ4580 ABCDEF(meet in the middle)

    题意 题目链接 Sol 发现abcdef是互不相关的 那么meet in the middle一下.先算出abc的,再算def的 注意d = 0的时候不合法(害我wa了两发..) #include&l ...

  3. codevs1735 方程的解数(meet in the middle)

    题意 题目链接 Sol 把前一半放在左边,后一半放在右边 meet in the middle一波 统计答案的时候开始想的是hash,然而MLE了两个点 实际上只要排序之后双指针扫一遍就行了 #inc ...

  4. 【BZOJ4800】[Ceoi2015]Ice Hockey World Championship (meet in the middle)

    [BZOJ4800][Ceoi2015]Ice Hockey World Championship (meet in the middle) 题面 BZOJ 洛谷 题解 裸题吧,顺手写一下... #i ...

  5. 【CF888E】Maximum Subsequence(meet in the middle)

    [CF888E]Maximum Subsequence(meet in the middle) 题面 CF 洛谷 题解 把所有数分一下,然后\(meet\ in\ the\ middle\)做就好了. ...

  6. 【CF912E】Prime Game(meet in the middle)

    [CF912E]Prime Game(meet in the middle) 题面 CF 懒得翻译了. 题解 一眼题. \(meet\ in\ the\ middle\)分别爆算所有可行的两组质数,然 ...

  7. CF888E Maximum Subsequence (Meet in the middle,贪心)

    题目链接 Solution Meet in the middle. 考虑到 \(2^{35}\) 枚举会超时,于是分成两半枚举(尽量平均). 然后不能 \(n^2\) 去匹配,需要用到一点贪心: 将数 ...

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

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

  9. cogs 304. [NOI2001] 方程的解数(meet in the middle)

    304. [NOI2001] 方程的解数 ★★☆   输入文件:equation1.in   输出文件:equation1.out   简单对比时间限制:3 s   内存限制:64 MB 问题描述 已 ...

随机推荐

  1. 安装配置nfs

    #yum -y install nfs-utils rpcbind #service rpcbind start#service nfs start #chkconfig --add rpcbind# ...

  2. 【算法】BFS+哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...

  3. mysql查看某个表的列名

    mysql查看某个表的列名mysql -uusername -p 输入密码按登录mysqlshow databases; 查看有哪些数据库use dbname; 选择数据库show tables:查看 ...

  4. 求和(NOIP2015)

    题目链接:求和 这道题不是很简单,因为数据并不是很小,常规计算会t. 这里引用chenleyu的解答(如果想要cgg原创解答,--改天吧): 这题相对是比较难的,首先我们要解读题目的意思 一条狭长的纸 ...

  5. HDU - 5658

    题意:给你一个字符串,给你Q次询问,每一次问你从l-r里有多少个回文串. 思路:len很小,所以直接遍历区间求就好了. /* gyt Live up to every day */ #include& ...

  6. Sencha extjs 的安装

    delphi 的母公司Idera 突然就把sencha extjs 收购了,这确实是一个很好的消息,意味着delphi 开始在web方面开始发力, 目前delphi 集成extjs 的呼声越来越强烈, ...

  7. 2018.11.07 NOIP模拟 分糖果(贪心)

    传送门 考虑 n = 2 时的情况:假定两个人分别为(a, b),(c, d),则当且仅当min(a,d) ≤ min(b,c)时,把(a, b)放在前面更优,否则把(c, d)放在前面更优 然后把n ...

  8. java 后台 post请求 携带参数 远程操作 调用接口

    package com.huayu.tizong.matchteam.util; import java.io.BufferedReader; import java.io.IOException; ...

  9. python 基础_ 打印输出 循环分支2

    一.在python3中的打印输出 1.输出字符串是print("hello world!!!") #输出字符串的时候可以是单引号括起来,也可以是双引号括起来.区别在于 2.输出变量 ...

  10. 20155205 2016-2017-2 《Java程序设计》第3周学习总结

    20155205 2016-2017-2 <Java程序设计>第3周学习总结 教材学习内容总结 第四章 定义类的两种方法(new和this的用法) 只要有一个类定义,编译程序就会产生一个. ...