您的位置:首页 > 其它

JZOJ 4841【NOIP2016提高A组集训第4场】平衡的子集

2016-11-02 17:24 381 查看

Description

夏令营有N个人,每个人的力气为M(i)。请大家从这N个人中选出若干人,如果这些人可以分成两组且两组力气之和完全相等,则称为一个合法的选法,问有多少种合法的选法?

Data Constraint

40%的数据满足:1<=M(i)<=1000;

对于100%的数据满足:2<=N<=20,1<=M(i)<=100000000

Solution

我们可以采用折半搜索。我们设一个人有三种状态:-1,0,1表示该人处在哪个队伍中或者不选。这样我们能算出前10个人能构出的所有和,然后我们将它的值存在一个哈希表里,同时该值向这10个人的二进制状态连一条边。再算后10个人时,直接在哈希表搜一下是否有相同的值即可。时间复杂度O(2∗310∗log310)。

代码

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const int maxn=50,maxn1=1048580;
int a[maxn],value[maxn1],er[maxn],h[maxn1][3],next[maxn1];
int n,i,t,j,k,l,x,sum,ans,p,num,q,q1;
bool bz[1025][1025];
int hash(int x){
int t=(x+maxn1*1000)%maxn1;
while (h[t][0]!=q1 && h[t][0]!=x)
t=(t+1)%maxn1;
return t;
}
void dg(int x,int y,int p){
if (x>n/2){
t=hash(y);
if (h[t][0]>=0){
k=h[t][2];
next[k]=++num;
value[num]=p;
h[t][2]=num;
}else h[t][1]=h[t][2]=++num,value[num]=p,h[t][0]=y;
return;
}
dg(x+1,y,p);
dg(x+1,y+a[x],p+er[x-1]);
dg(x+1,y-a[x],p+er[x-1]);
}
void dg1(int x,int y,int p){
int t,k;
if (x>n){
t=hash(y);
if (h[t][1]==q1) return;
for(k=h[t][1];k;k=next[k])
if (!bz[p][value[k]]) bz[p][value[k]]=true,ans++;
return;
}
dg1(x+1,y,p);
dg1(x+1,y+a[x],p+er[x-q]);
dg1(x+1,y-a[x],p+er[x-q]);
}
int main(){
//freopen("subset.in","r",stdin);freopen("subset.out","w",stdout);
freopen("data.in","r",stdin);
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
er[0]=1;
for (i=1;i<=10;i++)
er[i]=er[i-1]*2;
memset(h,128,sizeof(h));
q1=h[0][0];q=n/2+1;
dg(1,0,0);
dg1(q,0,0);
ans--;
printf("%d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: