poj3977 Subset 折半枚举

题意:给你一个含n(n<=35)个数的数组,让你在数组中选出一个非空子集,使其元素和的绝对值最小,输出子集元素的个数以及元素和的绝对值,若两个子集元素和相等,输出元素个数小的那个。

思路:如果直接暴力枚举,复杂度O(2^n),n为35时会超时,故可以考虑折半枚举,利用二进制将和以及元素个数存在两个结构体数组中,先预判 两个结构体是否满足题意,再将其中一个元素和取相反数后排序,因为总元素和越接近零越好,再二分查找即可,用lower_bound时考虑查找到的下标和他前一个下标,比较元素和以及元素个数,不断更新即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct Z{
long long int x;
int y;
bool operator < (const Z& b)const

    {
        if(x!=b.x)
        return x < b.x;
        return y<b.y;

    }
}a[300005],b[300005];
long long  int c[40];
long long int abs1(long long int x){
if(x<0)
    return -x;
return x;
}
int main(){
int n;
while((cin>>n)&&n!=0){
    for(int i=0;i<300005;i++)
       a[i].x=a[i].y=b[i].x=b[i].y=0;
    long long int sum=1e17;
    int ans=40;
    for(int i=0;i<n;i++)
        cin>>c[i];
    int n1=n/2;
    for(int i=0;i<1<<n1;i++){
        for(int j=0;j<n1;j++){
            if(i>>j&1&&(i!=0||j!=0)){
                a[i-1].x+=c[j];
                a[i-1].y++;
            }
        }

    }
    int n2=n-n1;
    for(int i=0;i<(1<<n2);i++){
        for(int j=0;j<n2;j++){
            if(i>>j&1&&(i!=0||j!=0)){
                b[i-1].x+=c[j+n1];
                b[i-1].y++;
            }
        }

    }
  for(int i=0;i<(1<<n1)-1;i++){
    if(abs1(a[i].x)<sum){
        sum=abs1(a[i].x);
        ans=a[i].y;
    }
    else if(abs1(a[i].x)==sum&&a[i].y<ans){
        ans=a[i].y;
        sum=abs1(a[i].x);
    }
  }
for(int i=0;i<(1<<n1)-1;i++)
    a[i].x=-a[i].x;
  for(int i=0;i<(1<<n2)-1;i++){
    if(abs1(b[i].x)<sum){
        sum=abs1(b[i].x);
        ans=b[i].y;
    }
    else if(abs1(b[i].x)==sum&&b[i].y<ans){
        ans=b[i].y;
        sum=abs1(b[i].x);
    }
  }

    sort(a,a+(1<<n1)-1);
    sort(b,b+(1<<n2)-1);
    for(int i=0;i<(1<<n1)-1;i++){
        int t=lower_bound(b,b+(1<<n2)-1,a[i])-b;
        if(t>0){
        if(abs1(b[t-1].x-a[i].x)<sum){
            sum=abs1(b[t-1].x-a[i].x);
            ans=b[t-1].y+a[i].y;
        }
        else if(abs1(b[t-1].x-a[i].x)==sum&&b[t-1].y+a[i].y<ans){
            sum=abs1(b[t-1].x-a[i].x);
            ans=b[t-1].y+a[i].y;
        }
        }
        if(t<(1<<n2)-1){
            if(abs1(b[t].x-a[i].x)<sum){
            sum=abs1(b[t].x-a[i].x);
            ans=b[t].y+a[i].y;
        }
        else if(abs1(b[t].x-a[i].x)==sum&&b[t].y+a[i].y<ans){
            sum=abs1(b[t].x-a[i].x);
            ans=b[t].y+a[i].y;
        }
        }



    }
cout<<sum<<" "<<ans<<endl;
}

}


阅读更多
文章标签: ACM poj 算法
个人分类: poj 双向搜索
上一篇poj2674 弹性碰撞 Linear world
下一篇poj2549 Sumsets双向搜索
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭