题意:n个数选若干个,使他们和的绝对值最小,如果存在多个解,选择所选的个数最少的。
题解:n为35,枚举所有情况肯定超时,可以枚举一半然后二分找另一半。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <map>
using namespace std;
typedef long long ll;
map<ll,ll> m;
ll a[50];
ll ABS(ll x)
{
if(x >= 0) return x;
else return -x;
}
int main()
{
int n;
while (scanf("%d",&n)!=EOF&&n)
{
m.clear();
for(int i=0;i<n;i++)scanf("%lld",&a[i]);
int n1=n/2;
int n2=n-n1;
int m1=(1<<n1),m2=(1<<n2);
pair<ll,ll> ans;
ans=make_pair(ABS(a[0]),1);
for(int i=0;i<m1;i++)
{
ll sum=0,num=0;
for(int j=0;j<n1;j++)
{
if(i&(1<<j))
{
sum+=a[j];num++;
}
}
if(num==0) continue;
ans=min(ans,make_pair(ABS(sum),num));
map<ll,ll>::iterator it=m.find(sum);
if(it!=m.end()){
it->second=min(it->second,num);
}
else{
m[sum]=num;
}
}
for(int i=0;i<m2;i++)
{
ll sum=0,num=0;
for(int j=0;j<n2;j++)
{
if(i&(1<<j))
{
sum+=a[n1+j];num++;
}
}
if(num==0) continue;
ans=min(ans,make_pair(ABS(sum),num));
map<ll,ll>::iterator it=m.lower_bound(-sum);
if(it!=m.end())
{
ans=min(ans,make_pair(ABS(sum+it->first),num+it->second));
}
if(it!=m.begin())
{
it--;
ans=min(ans,make_pair(ABS(sum+it->first),num+it->second));
}
}
printf("%lld %lld\n",ans.first,ans.second);
}
return 0;
}