折半搜索 [meet in the middle]

Learning

在使用搜索的时候,当n过大2n会超时,这时候可以用折半搜索,把时间复杂度降低到2n/2+1
在这里插入图片描述
具体方法:分别搜索前一半,把状态放入a数组,搜索后一半,把状态放入b数组,最后统计答案。
一般meet in the middle的难点主要在于最后答案的组合统计。
我们可以现将a或b数组sort,让其有序。然后通过枚举另一个数组中的状态,来实现统计答案。在这里插入图片描述


Practice

[洛谷 4799] 世界冰球锦标赛

题目链接

题意:给定n(<=40)场比赛门票各自的价格,和Bobek拥有的钱数,求观赛方案总数。
(如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。可以一场也不看)

思路:240的搜索肯定会超时,所以折半搜索。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=1<<21;
ll m,ans,a[50],suma[MAXN],sumb[MAXN];
int n,cnta,cntb;
void dfs(int t,int M,ll f[],ll s,int &cnt){
	if(t==M+1){
		f[++cnt]=s;
		return;
	}
	dfs(t+1,M,f,s,cnt);
	dfs(t+1,M,f,s+a[t],cnt);
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	int k=(1+n)/2,d;
	dfs(1,k,suma,0,cnta);//搜索前一半
	dfs(k+1,n,sumb,0,cntb);//搜索后一半
	sort(sumb+1,sumb+1+cntb);//合并
	for(int i=1;i<=cnta;i++){
		d=upper_bound(sumb+1,sumb+1+cntb,m-suma[i])-sumb-1;
		ans+=d;
	}
	cout<<ans;
}

[洛谷 3067] Balanced Cow Subsets

题目链接

题意:给n(<=20)个数,从中任意选出一些数,使这些数能分成和相等的两组。求有多少种选数的方案。

思路:这道题有三种状态:①不放入任何集合 ②放入左边集合 ③放入右边集合
[面向题解设计程序。。一知半解]

#include<cstdio>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define ll long long
int n,mid;
struct node{
	int v,f;
}suma[1<<22],sumb[1<<22];
int cnta,cntb,m[44],r;
bool vis[1<<23];
ll ans;
void dfs(int l,int flag,int sum,int t){
	if(l>r){
		if(t){
			suma[++cnta].v=sum;
			suma[cnta].f=flag;
		}else{
			sumb[++cntb].v=sum;
			sumb[cntb].f=flag;
		}
		return;
	}
	dfs(l+1,flag,sum,t);
	dfs(l+1,flag+(1<<(l-1)),sum+m[l],t);
	dfs(l+1,flag+(1<<(l-1)),sum-m[l],t);
}
inline bool cmp1(node x,node y){return x.v<y.v;}
inline bool cmp2(node x,node y){return x.v>y.v;}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",&m[i]);
	mid=(1+n)/2;
	r=mid;dfs(1,0,0,1);//折半搜索 
	r=n;dfs(mid+1,0,0,0);
	
	sort(suma+1,suma+1+cnta,cmp1);//排序 
	sort(sumb+1,sumb+1+cntb,cmp2);
	
	int l=1,r=1;
	while(l<=cnta&&r<=cntb){//双指针
		while(-suma[l].v<sumb[r].v&&r<=cntb)r++;
		int p=r;
		while(r<=cntb&&-suma[l].v==sumb[r].v){
			if(!vis[suma[l].f|sumb[r].f]){
				vis[suma[l].f|sumb[r].f]=1;
				ans++;
			}
			r++;
		}
		if(l<cnta&&suma[l].v==suma[l+1].v)r=p;
		l++;
	}
	cout<<ans-1;
}
	
	
	
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkscx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值