poj3977 Subset 折半枚举

原创 2017年01月02日 23:59:12

题意:给你一个含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;
}

}


版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

poj 3977 Subset

题意:在一个集合中找到一个非空子集使得这个子集元素和的绝对值尽量小,和绝对值相同时保证元素个数尽量小 分析:1.二分枚举的思想,先分成两个集合;    2.枚举其中一个集合中所有的子集并且存到数组中,...

poj3977 Subset(折半枚举)

题目大意: 给定N个整数组成的数列(N 思路: 如果单纯的枚举的话,这N个数分别有选和不选两种,所以一共有2^35个子集。很明显,会超时,但是我们可以将这个整数数列分成两半,可得每边最多18个,...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

折半枚举--poj3977

Language: Default Subset Time Limit: 30000MS   Memory Limit: 65536K Total Submis...

POJ3977 Subset 折半枚举

折半枚举,减小问题的规模。

poj3977

#include #include #include #include using namespace std; pair cor[1005]; int n,d,par[1005],rk[1005...

POJ2549 Sumsets 折半枚举

题目大意是,一个集合中有N个元素,找出最大的S,满足条件A+B+C=S,并且这四个数都属于该集合,N不超过1000.         如果直接枚举O(n^4)显然复杂度太高,将等式转化一下A+B=S...

poj 3977 Subset 折半枚举

题意: 给n(n 分析: 折半枚举。 代码: //poj 3977 //sep9 #include #include using namespace std; typedef long ...

poj 3977 Subset(折半枚举+状压dp)

题目一眼看上去就是状压但n太大,ya bu dong

POJ 3977Subset(枚举+二分)

Subset Time Limit: 30000MS   Memory Limit: 65536K Total Submissions: 1562   Accepted: 261 ...
  • opm777
  • opm777
  • 2014-05-20 17:43
  • 1107

《挑战程序设计竞赛》3.2.4 常用技巧-折半枚举 POJ2785 3977 2549

POJ2785http://poj.org/problem?id=2785题意输入n,表示a b c d 四个集合都有n个元素。之后每行输入4个集合中的一个元素。求这四个集合每个集合中拿出一个数相加等...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)