poj 3977 折半枚举二分搜索

来源:互联网 发布:mysql输入密码后闪退 编辑:程序博客网 时间:2024/04/29 14:42

题意:

从n(35)个数字(有正有负)中取出一个非空子集,使得其和的绝对值最小。

若最小的绝对值有多组,则选择非空子集元素个数最少的。


解析:

折半来枚举。

将35分成两半,第一半状态压缩,然后用map离散化每个sum和num。

然后第二半还是状态压缩完了在map里面找最接近-sum的数。

ps,真是超级喜欢hankcs的c++代码风格。

- - 12s


代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <climits>#include <cassert>#define LL long longusing namespace std;const int inf = 0x3f3f3f3f;const double eps = 1e-8;const double pi = 4 * atan(1.0);const double ee = exp(1.0);const int maxn = 30 + 10;LL a[maxn];LL Abs(LL x){    return x >= 0 ? x : -x;}int main(){#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    int n;    while (~scanf("%d", &n) && n)    {        for (int i = 0; i < n; i++)        {            scanf("%lld", &a[i]);        }        map<LL, int> mp;        // 未取绝对值的sum  <->  元素个数num        pair<LL, int> ans(Abs(a[0]), 1);        int inA = (n >> 1);        int stA = (1 << inA);        int inB = (n - (n >> 1));        int stB = (1 << inB);        for (int i = 0; i < stA; i++)        {            LL sum = 0;            int num = 0;            for (int j = 0; j < inA; j++)            {                if ((i >> j) & 1)                {                    sum += a[j];                    num++;                }            }            if (num == 0)                continue;            ans = min(ans, make_pair(Abs(sum), num));            map<LL, int>::iterator it = mp.find(sum);            if (it != mp.end())            {                it -> second = min(it -> second, num);            }            else            {                mp[sum] = num;            }        }        for (int i = 0; i < stB; i++)        {            LL sum = 0;            int num = 0;            for (int j = 0; j < inB; j++)            {                if ((i >> j) & 1)                {                    sum += a[inA + j];                    num++;                }            }            if (num == 0)                continue;            ans = min(ans, make_pair(Abs(sum), num));            map<LL, int>::iterator it = mp.lower_bound(-sum);            if (it != mp.end())            {                ans = min(ans, make_pair(Abs(sum + it -> first), num + it -> second));            }            if (it != mp.begin())            {                --it;                ans = min(ans, make_pair(Abs(sum + it -> first), num + it -> second));            }        }        printf("%lld %d\n", Abs(ans.first), ans.second);    }    return 0;}


0 0
原创粉丝点击