POJ - 3977 Subset

题意:求N个元素的非空子集和的绝对值最接近0,在此条件下子集大小尽量小,N≤35。

N/2是17和18,217和218都是1e5的数量级,是可以枚举的。

枚举后得到的集合记为P,Q,ans要么单独在P或者Q中,要么是两者的和。

把Q中所有元素反号以后,枚举P中元素pi,就是找一个最接近pi的qj。

所以只要排好序,值相同的保留集合最小的,然后维护两个单调的下标就好。(也可以二分

m = n/2,复杂度为O(mlogm)

POJ上编译abs比较奇怪,要自己写一个,不然CE

/*********************************************************
*            ------------------                          *
*   author AbyssalFish                                   *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<numeric>
using namespace std;

typedef long long ll;
inline ll Abs(const ll &v){ return v<0?-v:v; }

const int MAX_N = 35, half = 1<<17;
ll a[MAX_N], b[MAX_N];
int N;

struct Dat
{
    ll v; int c;
    Dat(){}
    Dat(ll v,int c):v(v),c(c){}
    bool operator < (const Dat& th) const {
        return Abs(v) < Abs(th.v) || (Abs(v) == Abs(th.v) && c < th.c);
    }
    Dat operator & (Dat &th){
        return Dat(v-th.v,c+th.c);
    }
}p[half], q[half<<1];

bool cmp (const Dat& ts, const Dat& th) {
    return ts.v < th.v || (ts.v == th.v && ts.c < th.c);
}

Dat ans;

int prepare(ll *x, Dat *y, int k)
{
    int s = 0;
    for(int S = (1<<k); --S; s++){
        y[s].v = y[s].c = 0;
        for(int i = 0; i < k; i++){
            if(S>>i&1){
                y[s].c++;
                y[s].v += x[i];
            }
        }
        ans = min(ans,y[s]);
    }
    sort(y,y+s,cmp);
    if(!s) return 0;
    k = 0;
    for(int i = 1; i < s; i++){
        if(y[i].v != y[k].v) {
            if(++k != i) y[k] = y[i];
        }
    }
    return k+1;
}

void solve()
{
    int k = N>>1;
    for(int i = 0; i < k; i++) {
        scanf("%I64d",a+i);
    }
    for(int i = 0; i < N-k; i++) {
        scanf("%I64d",b+i);
        b[i] = -b[i];
    }
    ans.v = b[0]; ans.c = 1;
    int sz1 = prepare(a,p,k);
    //sort(p,q+sz1,cmp);
    int sz2 = prepare(b,q,N-k);
    for(int i = 0, j = 0; i < sz1; i++){
        while(j < sz2 && p[i].v > q[j].v) j++;
        if(j) ans = min(ans,p[i]&q[j-1]);
        if(j < sz2) ans = min(ans,p[i]&q[j]);
    }
    printf("%I64d %d\n",Abs(ans.v), ans.c);
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    //cout<<half; 1e5
    while(scanf("%d",&N),N){
        solve();
    }
    return 0;
}

 

posted @ 2015-11-26 12:30  陈瑞宇  阅读(263)  评论(0编辑  收藏  举报