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; }