POJ3977Subset(折半枚举)

来源:互联网 发布:人体工学椅有用吗 知乎 编辑:程序博客网 时间:2024/06/11 16:56

链接:

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;}


原创粉丝点击