题目一眼看上去就是状压但n太大,压不动所以我们可以考虑把集合拆成2分,把2份分别状压,然后在遍历其中一份,由于要找喝的绝对值最小的情况,所以只要在另一个数组里尔分查找当前值的相反数最近的数,然后更新。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define LL long long
#define maxn 500005
using namespace std;
struct pi{
LL x;
int w;
}a[maxn],b[maxn];
LL c[37];
LL aabs(LL x){
if(x<0) x=-x;
return x;
}
int cmp(pi x,pi y){
if(x.x!=y.x)
return x.x<y.x;
return x.w<y.w;
}
int get(LL p,int n){
int le,ri,mid;
le=0;
ri=n-1;
while(le<=ri){
mid=(le+ri)/2;
if(b[mid].x<p) le=mid+1;
else ri=mid-1;
}
return le;
}
int main()
{
int i,j,n,x,to1,to2,f;
LL p,mm;
while(1){
scanf("%d",&n);
if(n==0) break;
for(i=0;i<n;i++) cin>>c[i];
if(n==1){
cout<<aabs(c[0])<<" 1"<<endl;
}
else{
to1=0;
to2=0;
for(i=1;i<1<<(n/2);i++){
p=0;
f=0;
for(j=0;j<n/2;j++){
if(i&(1<<j)){
p+=c[j];
f++;
}
}
a[to1].w=f;
a[to1++].x=p;
}
for(i=1;i<1<<(n-n/2);i++){
p=0;
f=0;
for(j=0;j<n-n/2;j++){
if(i&(1<<j)){
p+=c[j+n/2];
f++;
}
}
b[to2].w=f;
b[to2++].x=p;
}
sort(a,a+to1,cmp);
sort(b,b+to2,cmp);
mm=(LL)1<<62;
x=0;
for(i=0;i<to1;i++){
p=get(-a[i].x,to2);
if(p<to2){
if(aabs(a[i].x+b[p].x)<mm){
mm=aabs(a[i].x+b[p].x);
x=a[i].w+b[p].w;
}
else if(aabs(a[i].x+b[p].x)==mm&&a[i].w+b[p].w<x){
x=a[i].w+b[p].w;
}
}
if(p>0){
p--;
f=get(b[p].x,to2);
p=f;
if(aabs(a[i].x+b[p].x)<mm){
mm=aabs(a[i].x+b[p].x);
x=a[i].w+b[p].w;
}
else if(aabs(a[i].x+b[p].x)==mm&&a[i].w+b[p].w<x){
x=a[i].w+b[p].w;
}
}
}
for(i=0;i<to1;i++){
if(aabs(a[i].x)<mm){
mm=aabs(a[i].x);
x=a[i].w;
}
else if(aabs(a[i].x)==mm&&a[i].w<x){
x=a[i].w;
}
}
for(i=0;i<to2;i++){
if(aabs(b[i].x)<=mm){
if(aabs(b[i].x)<mm){
mm=aabs(b[i].x);
x=b[i].w;
}
else{
if(b[i].w<x){
x=b[i].w;
}
}
}
}
cout<<mm;
printf(" %d\n",x);
}
}
}