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

      

      

  • 相关阅读:
    HDU 4611 Balls Rearrangement 数学
    Educational Codeforces Round 11 D. Number of Parallelograms 暴力
    Knockout.Js官网学习(简介)
    Entity Framework 关系约束配置
    Entity Framework Fluent API
    Entity Framework DataAnnotations
    Entity Framework 系统约定配置
    Entity Framework 自动生成CodeFirst代码
    Entity Framework CodeFirst数据迁移
    Entity Framework CodeFirst尝试
  • 原文地址:https://www.cnblogs.com/inmoonlight/p/5766232.html
Copyright © 2020-2023  润新知