题目描述:
Given a list of N integers with absolute values no larger than 10 15, find a non empty subset of these numbers which minimizes the absolute value of the sum of its elements. In case there are multiple subsets, choose the one with fewer elements.
输入:
The input contains multiple data sets, the first line of each data set contains N <= 35, the number of elements, the next line contains N numbers no larger than 10 15 in absolute value and separated by a single space. The input is terminated with N = 0
输出:
For each data set in the input print two integers, the minimum absolute sum and the number of elements in the optimal subset.
样例输入:
1
10
3
20 100 -100
0
样例输出:
10 1
0 2
题目大意:
让你从n个数里面找任意个数(>0),使他们的和的绝对值最小,如果有多组和一样最小,输出最小和的绝对值且最小个数。
2^35,会超时,所以考虑折半枚举,减少时间复杂度,并用二分,查找绝对值最小的和
代码如下:
#include<stdio.h>
#include<algorithm>
#include<iostream>
const int maxn=1000005;
long long a[maxn];
int qq;
using namespace std;
struct node{
long long value;
int len;
}nod[maxn],cun[maxn];
bool cmp(node a,node b)
{
if(a.value==b.value)return a.len<b.len;
else return a.value<b.value;
}
int findd(long long what)
{
int lb=-1,ub=qq,mid;
while(ub-lb>1)
{
mid=(lb+ub)/2;
if(nod[mid].value>=what)ub=mid;
else lb=mid;
}
return ub;
}
int main()
{
int n;
while(1)
{
scanf("%d",&n);
long long ans=1e15+5,res=99999;//一开始写的是ans=9999999,然后一直wrong
if(n==0)
{
break;
}
int i,j,k;
for(i=0;i<n;i++)
{
scanf("%lld",&a[i]);
}
int m=n/2;
int s=1<<(n-m);
for(i=1;i<s;i++)
{
int tt=i;
long long temp=0;
int cnt=0;
for(j=m;j<n;j++)
{
if(tt&1)
{
temp+=a[j];
cnt++;
}
tt>>=1;
}
cun[i].value=temp;
cun[i].len=cnt;
}
sort(cun+1,cun+s,cmp);//后半段可能有的值
qq=2;
nod[0].value=0;
nod[0].len=0;//一定要有
nod[1].value=cun[1].value;
nod[1].len=cun[1].len;
for(i=2;i<s;i++)
{
if(cun[i].value!=nod[qq-1].value)
{
nod[qq].value=cun[i].value;
nod[qq++].len=cun[i].len;
}
}
sort(nod,nod+qq,cmp);
//减少重复
s=1<<m;
for(i=0;i<s;i++)
{
long long temp=0;
int cnt=0,tt=i;
for(j=0;j<m;j++)
{
if(tt&1)
{
temp+=a[j];
cnt++;
}
tt>>=1;
}
long long temp1=-temp;
int pos=findd(temp1);
int small=max(0,pos-1);
int big=min(pos+1,qq-1);
for(j=small;j<=big;j++)
{
long long en=nod[j].value+temp;
int tm=nod[j].len+cnt;
if(en<0)en=-en;
if(tm==0&&en==0)
continue;
if(en<ans||(en==ans&&tm<res))
{
ans=en;
res=tm;
}
}
}//前半段和后半段的叠加
printf("%lld %lld\n",ans,res);
}
return 0;
}