zoukankan      html  css  js  c++  java
  • Subset POJ

    题目描述

    Given a list of N integers with absolute values no larger than 10 15, 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.

    输出

    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 10 15 in absolute value and separated by a single space. The input is terminated with N = 0

    输入

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

    样例

    样例输入

    1
    10
    3
    20 100 -100
    0
    

    样例输出

    10 1
    0 2
    

    分析

    一句话题意:多组数据,以 0 为结尾,给你n个数,求出这n个数的一个非空子集,使子集中的数加和的绝对值最小,在此基础上子集中元素的个数应最小。

    N的最大值为35,暴力枚举肯定是要超时的,因为你要把235种情况都枚举出来,即使30秒的时限也是不够用的

    暴力枚举

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int maxn=50;
    ll a[maxn],ans;
    int n,cnt;
    ll mabs(ll x){
        if(x>0) return x;
        return -x;
    }
    void dfs(int now,ll tot,int xuan){
        if(xuan && mabs(tot)<ans){
            cnt=xuan;
            ans=mabs(tot);
        }
        else if(xuan && ans==mabs(tot)) cnt=min(cnt,xuan);
        for(int i=now;i<=n;i++){
            dfs(i+1,tot+a[i],xuan+1);
        }
    }
    int main(){
        while(scanf("%d",&n)!=EOF && n!=0){
            ans=0x3f3f3f3f3f3f3f3f;
            cnt=40;
            for(int i=1;i<=n;i++){
                scanf("%lld",&a[i]);
            }
            dfs(1,0,0);
            printf("%lld %d
    ",ans,cnt);
        }
        return 0;
    }
    

    其实我们可以考虑把n个数分成两部分,每个部分最多有218种结果

    我们先把前一半预处理处理出来,然后再枚举后一半

    对于每一个值,我们都要从前一半中找到与它的相反数最接近的数,然后两数相加取最小值

    需要注意的是,最优解也有可能出现在前一半或后一半,不一定是前一半中的数与后一半中的数相加的结果

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int maxn=50;
    int n,cnt=0;
    map<ll,ll> ma;
    //map记录当前值所选的最少元素个数
    ll a[maxn],jl[1<<20],ans=1e18;
    //jl数组存储前一半的结果,ans是最终结果
    ll abss(ll x){
        if(x>0) return x;
        return -x;
    }//绝对值函数
    ll solve(ll num){
        if(num<=jl[1]) return jl[1];
        else if(num>=jl[cnt]) return jl[cnt];
        else {
            ll l=1,r=cnt,mids;
            while(l<=r){
                mids=(l+r)/2;
                if(jl[mids]==num) break;
                else if(jl[mids]<num) l=mids+1;
                else r=mids-1;
            }
            if(jl[mids]==num){
                return num;
            }
            else if(abss(jl[l]-num)==abss(jl[r]-num)){
                if(ma[jl[l]]<ma[jl[r]]) return jl[l];
                return jl[r];
            }
            else if(abss(jl[l]-num)>=abss(jl[r]-num)){
               return jl[r];
            } else {
                return jl[l];
            }
        }
    }//模板,查找与x最接近的元素,要注意的是如果有多个元素与x最接近,取数的个数最小的那一个
    int main(){
        while(scanf("%d",&n)!=EOF && n!=0){
            cnt=0,ans=1e18;
            ma.clear();
            memset(a,0,sizeof(a));
            memset(jl,0,sizeof(jl));
            for(int i=1;i<=n;i++){
                scanf("%lld",&a[i]);
            }//初始化+输入
            if(n==1){
                printf("%lld 1
    ",abss(a[1]));
                continue;
            }//如果只有一个数,特判一下,因为前一半没有数,无法二分
            int le=n/2,re=n-le;
            int mmax=(1<<le)-1;
            ll id=0x3f3f3f3f3f3f3f3f;
            for(int i=1;i<=mmax;i++){
                ll now=0,js=0;
                for(int j=1;j<=le;j++){
                    if(i&(1<<(j-1))) now+=a[j],js++;
                }
                if(ma[now]) ma[now]=min(ma[now],js);
                //如果当前值已经出现过,元素个数取较小的
                else ma[now]=js,jl[++cnt]=now;
                //没有出现过,建立映射关系
                if(abss(now)<ans){
                    ans=abss(now);
                    id=js;
                }
                //如果答案更优,更新答案
                else if(abss(now)==ans){
                    id=min(id,js);
                }
                //如果答案相同,元素个数取较小的
            }
            //枚举前一半数,同时记录答案
            sort(jl+1,jl+cnt+1);
            //排序
            for(int i=1;i<(1<<re);i++){
                ll now=0,js=0;
                for(int j=1;j<=re;j++){
                    if(i&(1<<(j-1))){
                        now+=a[j+n/2],js++;
                    }
                }
                if(abss(now)<ans){
                    ans=abss(now);
                    id=js;
                }
                else if(abss(now)==ans){
                    id=min(id,js);
                }
                //同上
                ll aft=solve(-now);
                //二分找到与相反数最接近的数
                if(ans>abss(aft+now)){
                    ans=abss(aft+now);
                    id=js+ma[aft];
                }//如果答案更优,更新答案
                else if(ans==abss(aft+now)) id=min(id,js+ma[aft]);
                //如果和之前的答案相同,个数取较小的
            }
            printf("%lld %d
    ",ans,id);
        }
        return 0;
    }
    
  • 相关阅读:
    intellij idea for mac 2018 破解版
    Mac下Supervisor进程监控管理工具的安装与配置
    Mysql千万级大表优化策略
    php7实现基于openssl的加密解密方法
    openresty--centos7下开发环境安装
    webstorm下搭建编译less环境 以及设置压缩css
    七牛图片上传
    聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)
    joomla! 3.X 开发系列教程
    JSON反序列化接口的问题
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/12775672.html
Copyright © 2011-2022 走看看