zoukankan      html  css  js  c++  java
  • POJ3977:Subset——题解(三分+折半搜索)

    http://poj.org/problem?id=3977

    题目大意:有一堆数,取出一些数,记他们和的绝对值为w,取的个数为n,求在w最小的情况下,n最小,并输出w,n。

    ————————————————————

    两天时间,终于搞下。

    这题显然我们唯一能做到的只有暴力,但是2^35显然不可取……

    但是显然我们折半搜索的话复杂度只有2^18左右所以没问题。

    将数分成两堆,每一堆暴力求出所有情况并记录。

    然后枚举第一堆,三分第二堆求解即可。

    (为什么三分呢?因为绝对值啊,一定最优解是在函数的最低点,所以是单峰函数)

    ……思路挺简单是不是,但是注意以下几点:

    1.n不为零,这点需要特判。

    2.三分很容易写跪,具体怎么做看我代码。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const ll INF=8223372036854775807;
    inline ll abss(ll a){
        if(a<0)return -a;
        return a;
    }
    struct num{
        ll w;
        ll n;
    }mp1[300001],mp2[300001];
    ll a[36];
    int cnt1=0,cnt2=0;;
    bool in[36];
    void dfs(int n,int k,bool t){
        memset(in,0,sizeof(in));
        if(!t){
        mp1[++cnt1].w=mp1[cnt1].n=0;
        }
        for(int res=1;res<=((1<<k)-1);res++){
        int cnt=0;
        int lz=res;
        while(lz){
            in[++cnt]=lz-lz/2*2;
            lz/=2;
        }
        ll sum=0,num=0;
        for(int i=1;i<=cnt;i++){
            int j=i;
            if(t)j+=n/2;
            if(in[i]){
            sum+=a[j];
            num++;
            }
        }
        if(!t){
            mp1[++cnt1].w=sum;
            mp1[cnt1].n=num;
        }else{
            mp2[++cnt2].w=sum;
            mp2[cnt2].n=num;
        }
        }
        return;
    }
    bool cmp(num c,num d){
        if(c.w<d.w)return 1;
        if(c.w>d.w)return 0;
        if(c.n<d.n)return 1;
        return 0;
    }
    ll ans,cnt;
    void sanfen(int l,int r,num k){
        if(r-l<=2){
        ll t1=abss(k.w+mp2[l].w);
        ll t2=abss(k.w+mp2[r].w);
        ll t3=abss(k.w+mp2[(l+r)/2].w);
        ll c1=mp2[l].n+k.n;
        ll c2=mp2[r].n+k.n;
        ll c3=mp2[(l+r)/2].n+k.n;
        ll t,c;
        if(t1>t2||(t1==t2&&c1>c2)){
            t=t2;c=c2;
        }else{
            t=t1;c=c1;
        }
        if(t>t3||(t==t3&&c>c3)){
            t=t3;c=c3;
        }
        if(ans>t||(ans==t&&cnt>c)){
            ans=t;cnt=c;
        }
        return;
        }
        int mid1=(r+2*l)/3;
        int mid2=(l+2*r)/3;
        ll t1=abss(k.w+mp2[mid1].w);
        ll t2=abss(k.w+mp2[mid2].w);
        if(t1<t2||(t1==t2&&mp2[mid1].n+k.n<mp2[mid2].n+k.n)){
        sanfen(l,mid2-1,k);
        }else{
        sanfen(mid1+1,r,k);
        }
        return;
    }
    int main(){
        int n;
        while(scanf("%d",&n)!=EOF&&n){
        cnt1=0,cnt2=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        dfs(n,n/2,0);
        dfs(n,n-n/2,1);
        sort(mp2+1,mp2+cnt2+1,cmp);
        //for(int i=1;i<=cnt1;i++)printf("1 %lld %lld
    ",mp1[i].w,mp1[i].n);
        //for(int i=1;i<=cnt2;i++)printf("2 %lld %lld
    ",mp2[i].w,mp2[i].n);
        ans=INF;cnt=INF;
        for(int i=1;i<=cnt1;i++){
            sanfen(1,cnt2,mp1[i]);
            if(i>1){
            if(ans>abss(mp1[i].w)||(ans==abss(mp1[i].w)&&cnt>mp1[i].n)){
                ans=abss(mp1[i].w);cnt=mp1[i].n;
            }
            }
        }
        printf("%lld %lld
    ",ans,cnt);
        }
        return 0;
    }
  • 相关阅读:
    计算机二进制总结
    java-集合排序,队列,散列表map以及如何遍历
    java-Collection,List简单使用与方法/(集合使用-中)
    java-Date类与集合(上)
    java-正则、object中的两个方法的使用
    java-注释、API之字符串(String)
    Java-面向对象三大特征、设计规则
    java-多态、内部类
    java-修饰词、抽象类、抽象方法
    java-重载、包修饰词以及堆栈管理
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8031418.html
Copyright © 2011-2022 走看看