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

      

      

  • 相关阅读:
    ng-深度学习-课程笔记-1: 介绍深度学习(Week1)
    java发送http请求和多线程
    Spring Cloud Eureka注册中心(快速搭建)
    Spring boot集成Swagger2,并配置多个扫描路径,添加swagger-ui-layer
    springboot在idea的RunDashboard如何显示出来
    Oracle 中select XX_id_seq.nextval from dual 什么意思呢?
    mysql类似to_char()to_date()函数mysql日期和字符相互转换方法date_f
    MySQL的Limit详解
    HikariCP 个人实例
    NBA-2018骑士季后赛
  • 原文地址:https://www.cnblogs.com/inmoonlight/p/5766232.html
Copyright © 2011-2022 走看看