[TJOI2010]分金币

现在有n枚金币,它们可能会有不同的价值,现在要把它们分成两部分,要求这两部分金币数目之差不超过1,问这样分成的两部分金币的价值之差最小是多少?

meet in the middle 搞定

#include<bits/stdc++.h>
using namespace std;
int t,n,a[31];
struct node{
    int c;
    long long v;
}b[1000001],b1[1000001];
int r[1000001],cnt[1000001];
int cmp(node x,node y){
    if(x.c==y.c)return x.v<y.v;
    else return x.c<y.c;
}
int l[1000001],val[1000001];
int calc(int x,int t){
    int i;
    int ans=0;
    for(i=0;i<15;i++)ans+=(x>>i)&1?a[i+t]:0;
    return ans;
}
int main(){
    int i;
    cin>>t;
    for(i=0;i<(1<<15);i++)cnt[i]=cnt[i>>1]+(i&1);
    while(t--){
        scanf("%d",&n);
        long long sum=0;
        for(i=0;i<n;i++)scanf("%d",&a[i]),sum+=a[i];
        int m=n>>1,k=n-m; 
        for(i=0;i<(1<<m);i++)b[i]=(node){cnt[i],calc(i,0)};
        sort(b,b+(1<<m),cmp);
        for(i=0;i<1<<m;i++)val[i]=b[i].v;
        l[0] = 0;l[m + 1] = 1 << m;
        for(i=1;i<1<<m;i++)if(b[i].c!=b[i-1].c)l[b[i].c]=i;
        long long ans=1e15;
        for(i=0;i<(1<<k);i++){
            int c=(m-cnt[i]),w=calc(i,m);
            if(~c){
                int pos=lower_bound(val+l[c],val+l[c+1],sum/2-w)-val;
                if(pos<l[c+1])ans=min(ans,abs(2*(val[pos]+w)-sum));
                if(pos>l[c])ans=min(ans,abs(2*(val[pos-1]+w)-sum));    
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zbsakioi/p/11032685.html