POJ-3977-折半枚举,二分

来源:互联网 发布:java中的getclass方法 编辑:程序博客网 时间:2024/05/29 11:09

题目大意:有n个数,要求它的一个子集,使得这个子集的和的绝对值最小,如果有多种情况,选个数最少的那种;

题目解析:直接暴力枚举肯定超时,所以先枚举一半,另一半在枚举的过程中可以二分前一半;

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cmath>#include<map>using namespace std;typedef long long ll;int n;ll a[50];ll Abs(ll x){    if(x<0) return -x;    return x;}int main(){    while(scanf("%d",&n)!=EOF&&n)    {        map<ll,int>m;        map<ll,int>::iterator it;        for(int i=0;i<n;i++)        {            scanf("%lld",&a[i]);        }        pair<ll,int>ans(Abs(a[0]),1);        for(int i=1;i<(1<<(n/2));i++)        {            ll sum=0;            int cnt=0;            for(int j=0;j<n/2;j++)            {                if(i&(1<<j))                {                    sum+=a[j];                    cnt++;                }            }            ans=min(ans,make_pair(Abs(sum),cnt));            if(m[sum])                m[sum]=min(m[sum],cnt);            else                m[sum]=cnt;        }        for(int i=1;i<(1<<(n-n/2));i++)        {            ll sum=0;            int cnt=0;            for(int j=0;j<(n-n/2);j++)            {                if(i&(1<<j))                {                    sum+=a[j+n/2];                    cnt++;                }            }            ans=min(ans,make_pair(Abs(sum),cnt));            it=m.lower_bound(-sum);            if(it!=m.end())            {                ans=min(ans,make_pair(Abs(sum+it->first),cnt+it->second));            }            if(it!=m.begin())            {                it--;                ans=min(ans,make_pair(Abs(sum+it->first),cnt+it->second));            }        }        printf("%lld %d\n",ans.first,ans.second);    }    return 0;}


0 0
原创粉丝点击