您的位置:首页 > 其它

POJ - 3977 Subset

2015-11-26 12:30 507 查看
题意:求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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: