链接:
http://poj.org/problem?id=3977
题意:
给你n个数,n最大35,让你从中选几个数,不能选0个,使它们和的绝对值最小
如果有一样的,取个数最小的
题解:
先枚举前一半的所有情况 用map记录下来,
然后再枚举后一半的所有情况,再在前一半记录的map里面找相加的和 与0最接近的
不过map居然也可以用lower_bound!
#include <map>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define pli pair<ll, int>
map<ll, int> mp;
ll a[40];
ll labs(ll x)
{
return x >= 0 ? x : -x;
}
int main()
{
int n;
while(cin >> n, n)
{
mp.clear();
for(int i = 0; i < n; i++) cin >> a[i];
int n1 = n / 2, n2 = n - n1;
pli ans = pli(1e18, 0);
for(int i = 1; i < (1<<n1); i++)
{
ll sum = 0;int num = 0;
for(int j = 0; j < n1; j++) if(i&(1<<j)) sum += a[j], num++;
if(!mp[sum] || num < mp[sum]) mp[sum] = num;
pli t = pli(labs(sum), num);
if(t < ans) ans = t;
}
for(int i = 1; i < (1<<n2); i++)
{
ll sum = 0;int num = 0;
for(int j = 0; j < n2; j++) if(i&(1<<j)) sum += a[n1+j], num++;
pli t = pli(labs(sum), num);
if(t < ans) ans = t;
map<ll, int>::iterator k = mp.lower_bound(-sum);
if(k != mp.end())
{
t = pli(labs((*k).first + sum), num + (*k).second);
if(t < ans) ans = t;
}
if(k != mp.begin()) --k;
if(k != mp.end())
{
t = pli(labs((*k).first + sum), num + (*k).second);
if(t < ans)
ans = t;
}
}
cout << ans.first << " " << ans.second << endl;
}
return 0;
}