zoukankan      html  css  js  c++  java
  • POJ 3977 Subset

    Subset
    Time Limit: 30000MS   Memory Limit: 65536K
    Total Submissions: 3161   Accepted: 564

    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

    Source

     
     
     
    解析:折半枚举。N个元素的集合,子集有2N个,除去空集,有2N-1个子集。而N可达35,235-1这个数就很大了,即使给了30000ms,直接枚举也会超时,可以考虑折半枚举。把集合分为两部分,这样两个小集合元素规模至多为18,枚举量为218,在可以承受的范围内。这样就得到了两个可枚举的集合A和B,最终的结果来源于这三种情况:子集的元素只取自于A、子集的元素只取自于B、子集的元素取自A和B。对于子集的元素只取自于A、子集的元素只取自于B这两种情况,我们在枚举的时候不断更新就可以了。对于子集的元素取自A和B这种情况,我们在枚举B(假设得到的子集中所有元素的和为sum)时,最佳情况为在A中找到-sum,这样我们在二分查找时,在-sum附近进行更新即可。另外,POJ暂不支持64位的abs,自己写一个吧^_^
     
     
     
    #include <cstdio>
    #include <map>
    #define ll long long
    using namespace std;
    
    int n;
    ll a[40];
    
    ll ll_abs(ll x)
    {
        return x >= 0 ? x : -x;
    }
    
    void solve()
    {
        map<ll, int> mp;
        map<ll, int>::iterator it;
        pair<ll, int> res(ll_abs(a[0]), 1); //初始化结果为第一个元素
        for(int i = 1; i < 1<<(n/2); ++i){  //枚举区间为[1, 2^n),当i为0时,子集为空
            ll sum = 0;
            int num = 0;
            for(int j = 0; j < n/2; ++j){   //按位枚举
                if((i>>j)&1){
                    sum += a[j];
                    ++num;
                }
            }
            res = min(res, make_pair(ll_abs(sum), num));    //子集的元素只取自于A
            it = mp.find(sum);
            if(it != mp.end())
                it->second = min(it->second, num);
            else
                mp[sum] = num;
        }
        for(int i = 1; i < 1<<(n-n/2); ++i){
            ll sum = 0;
            int num = 0;
            for(int j = 0; j < n-n/2; ++j){
                if((i>>j)&1){
                    sum += a[n/2+j];
                    ++num;
                }
            }
            res = min(res, make_pair(ll_abs(sum), num));    //子集的元素只取自于B
            it = mp.lower_bound(-sum);  //查找与-sum最相近的值
            if(it != mp.end())  //可能在该位置
                res = min(res, make_pair(ll_abs(it->first+sum), it->second+num));
            if(it != mp.begin()){  //可能在该位置的前一个位置
                --it;
                res = min(res, make_pair(ll_abs(it->first+sum), it->second+num));
            }
        }
        printf("%I64d %d
    ", res.first, res.second);
    }
    
    int main()
    {
        while(scanf("%d", &n), n){
            for(int i = 0; i < n; ++i)
                scanf("%I64d", &a[i]);
            solve();
        }
        return 0;
    }
    

      

      

  • 相关阅读:
    css最简单的在背景图片上显示模糊背景色的方法
    css添加网格背景
    获取bing必应图片
    JavaScript超过一定高度导航添加类名
    6行css就可以解决的瀑布流布局的方法
    css实现背景图横向滚动
    JavaScript根据一个元素的显示隐藏控制另一个元素的显示和隐藏
    JavaScript判断地址栏链接与导航栏链接是否一致并给导航添加class
    JavaScript实现选中文字自动复制
    Day 74 算法基础(二)
  • 原文地址:https://www.cnblogs.com/inmoonlight/p/5766232.html
Copyright © 2011-2022 走看看