双向搜索学习笔记
好像又叫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; }
深深地感到自己的弱小。