我上网一搜折半搜索,结果跳出来的为什么全是二分??????????????????????????气死我了。
回归正题,折半搜索指将整个搜索过程分为两部分,并对两部分分别进行搜索,最后得到两个答案序列,将这两个答案序列进行合并,得到最终的答案。这样可以大大降低复杂度,比如原来的这样就变成了。来看一些题吧。
P4799:
很经典的题目了。首先,可以状压枚举每种情况,但是TLE,所以考虑折半搜索。将前一半的搜索状态存入a数组,后一半存入b数组。难点主要在于最后答案的组合统计。我们可以现将a或b数组sort,然后通过枚举另一个数组中的状态,来实现统计答案。令pos为当前枚举的x的第一个,那么在pos之前的位置都可以贡献答案,所以ans+=pos-1。那么找pos的工程可以用upper_bound()实现。
#include
using namespace std;
vector l,r;
long long ticket[100];
long long n,m,mid;
void dfs1(long long step, long long cost){
if(cost>m)
return;
if(step>mid){
l.push_back(cost);
return;
}
dfs1(step+1,cost+ticket[step]);
dfs1(step+1,cost);
}
void dfs2(long long step, long long cost){
if(cost>m)
return;
if(step>n){
r.push_back(cost);
return;
}
dfs2(step+1,cost+ticket[step]);
dfs2(step+1,cost);
}
int main(){
cin>>n>>m;
mid=(n+1)>>1;
for(long long i=1;i<=n;i++)
cin>>ticket[i];
dfs1(1,0);
dfs2(mid+1,0);
sort(r.begin(),r.end());
long long ans=0;
for(auto x:l)
ans+=upper_bound(r.begin(),r.end(),m-x)-r.begin();
cout<
P3067:
很不显然,这题比世界冰球锦标赛难一些。
当然,上来考虑(有3种状态:不放入任何集合,放入左边,放入右边)暴力,但显然大于1E9,你会得到一个完美的TLE。在搜索时我们可以0,1,-1来表示,像这样↓
dfs(dep+1,sum);
dfs(dep+1,sum+a[dep]);
dfs(dep+1,sum-a[dep]);
但是我们得到的答案可能会有重复,因为我们可能把一个数选入左集合或右集合,但是都加入了状态,所以我们需要判重。所以搜索时还要去记录状态,用一个vis数组判重。
if(!vis[a[l].sta|b[r].sta])
vis[a[l].sta|b[r].sta]=1;//sta记录二进制的选数状态 1表示选 0表示没选
最后要统计答案,排序后双指针扫描一遍即可。
#include
using namespace std;
int n,a[50],cntl,cntr,ans;
bool vis[1<<22];
struct node{
int sta,val;
}l[1<<22],r[1<<22];
bool cmpl(node x,node y){
return x.valy.val;
}
void dfs1(int x,int tot,int sta){
if(x>(n>>1)){
cntl++;
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)));
}
void dfs2(int x,int tot,int sta){
if(x>n){
cntr++;
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)));
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
dfs1(1,0,0);
dfs2((n>>1)+1,0,0);
sort(l+1,l+cntl+1,cmpl);
sort(r+1,r+cntr+1,cmpr);
for(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++){
int sta=(l[i].sta|r[pos].sta);
if(!vis[sta]){
vis[sta]=1;
ans++;
}
}
}
cout<
注意,最后别忘了把0的那种方案减去。(我没减是因为ans设为-1,已经被我改了)
以上就是本期的全部内容了,我们下期再见!
温馨提示:本期的全部代码都有问题,请不要无脑Ctrl C+Ctrl V