2017-08-01 21:45:19
writer:pprp
题目:
• POJ 3977
• 给定n个数,求一个子集(非空)
• 使得子集内元素和的绝对值最小
• n ≤ 35
AC代码如下:(难点:枚举出sum)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
const int maxn = 40;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n;
ll a[maxn];
int Min = INF;
ll ll_abs(ll x)
{
return x>=0?x:-x;
}
int main()
{
while(cin >> n && n)
{
memset(a,0,sizeof(a));
for(int i = 0 ; i < n ; i++)
{
cin >> a[i];
}
map<ll, int>mp; //sum -> cnt
pair<ll,int>ans(ll_abs(a[0]),1); //储存最优解
for(int i = 0 ; i < (1<<(n/2)) ; i++)
{
ll sum = 0;
int cnt = 0;
for(int j = 0 ; j < (n/2) ; j++)
{
if((i>>j)&1)
{
sum += a[j];
cnt++;
}
}
if(cnt == 0)
continue;
ans = min(ans,make_pair(ll_abs(sum),cnt));
map<ll, int>::iterator it = mp.find(sum);
if(it != mp.end())
{
//取更小的元素个数
it->second = min(it -> second, cnt);
}
else
mp[sum] = cnt;
}
for(int i = 0 ; i < (1 << (n - n / 2)) ; i++)
{
ll sum = 0;
int cnt = 0;
for(int j = 0 ; j < (n - n / 2) ; j++)
{
if((i>>j)&1)
{
sum += a[n / 2 + j];
cnt++;
}
}
if(cnt == 0) continue;
ans = min(ans, make_pair(ll_abs(sum), cnt));
map<ll, int> ::iterator it = mp.lower_bound(-sum);
if(it != mp.end())
ans = min(ans, make_pair(ll_abs(it->first + sum), it->second + cnt));
if(it != mp.begin())
{
it--;
ans = min(ans, make_pair(ll_abs(it->first + sum), it->second + cnt) );
}
}
cout << ans.first <<" " << ans.second << endl;
}
return 0;
}