双向搜索学习笔记

好像又叫meet in the middle

所谓双向搜索,简而言之,就是先搜一半,再搜另一半,然后试着将两半拼合。

具体看例题,你将会有更深的体会。

例题一:世界冰球锦标赛

网址:https://www.luogu.com.cn/problem/P4799

如果洛谷很卡的话,还有一个网址:https://loj.ac/problem/2789

朴素算法:2^n的搜索

双向搜索:现将之前的n/2搜出来,排序,再搜后n/2,二分查找。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[50],ans;
int s[2000000],tot;
inline void dfs(int pos,int sum){
    if(sum>m)return;
    if(pos==n/2+1){
        s[++tot]=sum;//记录结果
        return;
    }
    dfs(pos+1,sum);
    dfs(pos+1,sum+a[pos]);
}
inline void solve(int pos,int sum){
    if(sum>m)return;
    if(pos==n+1){//二分查找之前有多少个结果加上这个小于等于m
        int l=1,r=tot,rec;
        while(l<=r){
            int mid=(l+r)>>1;
            if(sum+s[mid]<=m){
                rec=mid;
                l=mid+1;
            }else r=mid-1;
        }
        ans+=rec;
        //ans+=upper_bound(s+1,s+1+tot,m-sum)-s-1;
        return;
    }
    solve(pos+1,sum);
    solve(pos+1,sum+a[pos]);
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    sort(a+1,a+1+n);
    dfs(1,0);//预处理前一半
    sort(s+1,s+1+tot);//将预处理的结果排序
    //for(int i=1;i<=tot;i++)
    //    printf("%lld ",s[i]);
    solve(n/2+1,0);//搜索后一半
    printf("%lld\n",ans);
    return 0;
} 

 例题二:[USACO12OPEN]Balanced Cow Subsets G

网址:https://www.luogu.com.cn/problem/P3067

具体请参照洛谷上的题解,我不知道怎么写,只好给你们转个 链接

还有代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2*1e6+10;
vector<int>g[maxn];
map<int,int>p;
int n,a[maxn],ti;
inline void dfs(int pos,int sum,int zt){
    if(pos==n/2+1){
        if(!p[sum])p[sum]=++ti;
        g[p[sum]].push_back(zt);
        return;
    }
    dfs(pos+1,sum,zt);
    dfs(pos+1,sum+a[pos],zt|(1<<pos-1));
    dfs(pos+1,sum-a[pos],zt|(1<<pos-1));
}
int ans,f[maxn];
inline void solve(int pos,int sum,int zt){
    if(pos==n+1){
        int x=p[sum];
        for(int i=0;i<g[x].size();i++)
            f[g[x][i]|zt]=1;
        return;
    }
    solve(pos+1,sum,zt);
    solve(pos+1,sum+a[pos],zt|(1<<pos-1));
    solve(pos+1,sum-a[pos],zt|(1<<pos-1));
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    dfs(1,0,0);
    solve(n/2+1,0,0);
    for(int i=1;i<(1<<n);i++)
        ans+=f[i];
    printf("%lld\n",ans);
    return 0;
}

深深地感到自己的弱小。

posted @ 2020-03-23 13:06  syzf2222  阅读(539)  评论(0编辑  收藏  举报