Subset POJ - 3977(折半枚举+二分查找)

题目来源:Subset

Description

Given a list of N integers with absolute values no larger than 1015, find a non empty subset of these numbers which minimizes the absolute value of the sum of its elements. In case there are multiple subsets, choose the one with fewer elements.

Input

The input contains multiple data sets, the first line of each data set contains N <= 35, the number of elements, the next line contains N numbers no larger than 1015 in absolute value and separated by a single space. The input is terminated with N = 0
Output

For each data set in the input print two integers, the minimum absolute sum and the number of elements in the optimal subset.

Sample Input

1
10
3
20 100 -100
0

Sample Output

10 1
0 2

题意

给定n个数,求这n个数所构成的集合的某个子集,使得这个子集中所有数和的绝对值最小,输出和的绝对值以及集合中数的个数。

思路

n是小于35的,直接枚举集合复杂度为2n,超时。可以枚举前n/2个数的集合,然后枚举后n/2个数的集合再在前面的集合中二分查找,这样复杂度为2(n/2) log(n/2)。

代码

#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
inline long long Abs(long long x)
{
    return x > 0 ? x : -x;
}
int n;
long long a[40];
int main()
{
    while (cin >> n)
    {
        if (n == 0)
            break;
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        map<long long, int>mmp;
        long long ans = Abs(a[1]);
        int len = n;
        for (int i = 1; i < (1 << (n / 2)); ++i)
        {
            long long sum = 0;
            int j = i, cnt = 0, pos = 1;
            while (j && pos <= n / 2)
            {
                if (j & 1)
                {
                    sum += a[pos];
                    cnt++;
                }
                j >>= 1;
                pos++;
            }
            if (Abs(sum) < ans)
            {
                ans = Abs(sum);
                len = cnt;
            }
            else if (Abs(sum) == ans)
            {
                len = min(len, cnt);
            }

            if (mmp[sum])
                mmp[sum] = min(mmp[sum], cnt);
            else
                mmp[sum] = cnt;
        }
        for (int i = 1; i < (1 << (n - n / 2)); ++i)
        {
            long long sum = 0;
            int cnt = 0, pos = 1, j = i;
            while (j && pos + n / 2 <= n)
            {
                if (j & 1)
                {
                    sum += a[pos + n / 2];
                    cnt++;
                }
                j >>= 1;
                pos++;
            }
            
            if (Abs(sum) < ans)
            {
                ans = Abs(sum);
                len = cnt;
            }
            else if (Abs(sum) == ans)
            {
                len = min(len, cnt);
            }
            map<long long, int>::iterator it = mmp.lower_bound(-sum);
            if (it != mmp.end())
            {
                if (Abs(sum + it->first) < ans)
                {
                    ans = Abs(sum + it->first);
                    len = cnt + it->second;
                }
                else if (Abs(sum + it->first) == ans)
                {
                    len = min(len, cnt + it->second);
                }
            }
            if (it != mmp.begin())
            {
                it--;
                if (Abs(sum + it->first) < ans)
                {
                    ans = Abs(sum + it->first);
                    len = cnt + it->second;
                }
                else if (Abs(sum + it->first) == ans)
                {
                    len = min(len, cnt + it->second);
                }
            }
        }
        cout << Abs(ans) << ' ' << len << endl;
    }
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容