• POJ 3977 折半枚举


    题目链接:http://poj.org/problem?id=3977

    前言

    如无法区分折半枚举,二分,请点击这里

    分析

    这题我感觉出了是用枚举,毕竟数据范围很小,但是,集合中每个元素都有可能被选或者不被选,根据计数原理应该会有(2^{35}-1)种情况,需要刨除空集,枚举显然是会T掉,那怎么办呢?
    考虑选出来的集合有几种情况,有可能都在前一半,有可能都在后一半,还有可能前后都有,前后都有的情况可以拆分成前两种情况, 所以优先考虑前两种,35的一半可以取17,发现(2^{17})是不会T的,我们只要每次枚举一半,然后就能解决这个问题,这就是如题所说的折半枚举。
    使用上述方法可以解决前两种情况,那前后都有呢?前后都有的必然是由前一半的一个子集和后一半的一个子集构成,因为要绝对值最小,所以要使这个集合接近于0,因此枚举前一个集合的每个子集,然后找到一个子集的和与这个子集的相反数最接近的集合,把他们当作答案就行。
    这道题来说的话,感觉如果不知道折半枚举这种思想,做起来还是挺困难的 ,有的时候暴力并不是没什么用,毕竟人家的另一个名字叫朴素算法,也许有的时候想不出什么高端数据结构,什么转移方程,不如写个暴力看看,也许优化一下就能A,就算A不了也会有很多过程分吧,毕竟OI不像ACM那样只算AC的题目,也许10分就能拉开很大差距。

    #include<cstdio> 
    #include<cstring> 
    #include<algorithm> 
    #include<map> 
    #define ll long long 
    const int N=40; 
    using namespace std; 
    ll a[N],n; 
    pair<ll,ll> ans; 
    map<ll,ll> p; 
    map<ll,ll>::iterator it; 
    ll abs(ll x){ 
        return x>0?x:-x; 
    } 
    void calc(){ 
        for(ll i=1;i<(1<<n/2);i++){ 
            ll t=i,sum=0,len=0; 
            for(ll j=n/2-1;j>=0;j--) 
                if(t&(1<<j))sum+=a[j],len++; 
            ll tep=abs(sum); 
            if(tep<ans.first || (tep==ans.first&&len<ans.second)) ans=make_pair(tep,len); 
            if(p[sum]>0)p[sum]=min(p[sum],len); 
            else p[sum]=len; 
        } 
        for(ll i=1;i<(1<<(n-n/2));i++){ 
            ll t=i,sum=0,len=0; 
            for(ll j=n-1;j>=n/2;j--){ 
                ll v=j-n/2; 
                if(t&(1<<v))sum+=a[j],len++; 
            } 
            ll tep=abs(sum); 
            if(tep<ans.first||(tep==ans.first&&len<ans.second)) 
                ans=make_pair(tep,len); 
            it=p.lower_bound(-sum); 
            if(it!=p.end()){ 
                ll val=abs((*it).first+sum),l=(*it).second+len;     
                if(ans.first>val||(ans.first==val&&l<ans.second)) 
                ans=make_pair(val,l); 
            } 
            if(it!=p.begin()){ 
                it--; 
                ll val=abs((*it).first+sum),l=(*it).second+len; 
                if(ans.first>val||(ans.first==val&&l<ans.second)) ans=make_pair(val,l);
            } 
        } 
        printf("%lld %lld
    ",ans.first,ans.second); 
    } 
    int main(){ 
        while(~scanf("%lld",&n)){ 
            if(n==0)return 0; 
            p.clear(); 
            for(int i=0;i<n;i++) 
                scanf("%lld",&a[i]); 
            ans=make_pair(abs(a[0]),1); 
            calc(); 
        } 
    }
    
  • 相关阅读:
    c# 时间操作
    JAVA file文件操作
    HttpServletRequest 转换成MultipartHttpServletRequest
    【日常笔记】java spring 注解读取文件
    【日常笔记】mybatis 处理 in 语句的使用
    购物车小程序
    Python中的r+和a+
    markdown基本语法
    markdown箭头的处理
    markdown中如何插入公式
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12778899.html
Copyright © 2020-2023  润新知