JZOJ 平衡的子集

Description

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

Input

第一行一个整数N,表示人数。
接下来N行,每行一个整数M(i)

Output

输出一行一个整数,表示一共多少种选法。

Sample Input

4
1
2
3
4

Sample Output

3
样例解释:
第一种选出{1,2,3},分成{1,2}和{3}两组;
第二种选出{1,3,4},分成{1,3}和{4}两组;
第三种选出{1,2,3,4},分成{1,4}和{2,3}两组。

Data Constraint

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

解题思路

折半搜索,先从1搜到n/2,每个有选到A组,选到B组,不选三种选择,最后将总和与状态记下来,n/2+1 ~ n搜一遍同样操作,最后排个序统计答案。

代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;
const int MAXN = 25;
typedef long long LL;

struct Data{
    LL sum;
    LL S;
}data1[1<<20],data2[1<<20];

int a[MAXN],n,k,cnt1,cnt2,ans;
bool vis[(1<<20)+2000];

inline void dfs1(int x,int Sum,int s){
    if(x==k+1) {
        data1[++cnt1].S=s;
        data1[cnt1].sum=Sum;
        return ;
    }
    dfs1(x+1,Sum+a[x],s+(1<<(n-x)));
    dfs1(x+1,Sum-a[x],s+(1<<(n-x)));
    dfs1(x+1,Sum,s);
}

inline void dfs2(int x,int Sum,int s){
    if(x==n+1){
        data2[++cnt2].S=s;
        data2[cnt2].sum=Sum;
        return ;
    }
    dfs2(x+1,Sum+a[x],s+(1<<(n-x)));
    dfs2(x+1,Sum-a[x],s+(1<<(n-x)));
    dfs2(x+1,Sum,s);
}

inline bool cmp(Data A,Data B){
    return A.sum<B.sum;
}

int main(){
    freopen("subset.in","r",stdin);
    freopen("subset.out","w",stdout);
    scanf("%d",&n);
    for(register int i=1;i<=n;i++) scanf("%d",&a[i]); 
    k=n/2;dfs1(1,0,0);dfs2(k+1,0,0);
    sort(data1+1,data1+1+cnt1,cmp);
    sort(data2+1,data2+1+cnt2,cmp);
    int i=1,j=1;data1[++cnt1].sum=data2[++cnt2].sum=1e9;

    while(i<cnt1 && j<cnt2){
        if(data1[i].sum<data2[j].sum) i++;
        else if(data1[i].sum>data2[j].sum) j++;
        else {
            int l1=i,r1=i,l2=j,r2=j;
            while(data1[r1].sum==data1[l1].sum) r1++;
            while(data2[r2].sum==data2[l2].sum) r2++;
            for(register int p=l1;p<r1;p++)
                for(register int q=l2;q<r2;q++){
                    if(vis[data1[p].S+data2[q].S]) continue;
                    ans++;vis[data1[p].S+data2[q].S]=1;
                }
            i=r1,j=r2;
        }
    }cout<<ans-1<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/sdfzsyq/p/9676902.html