zoukankan      html  css  js  c++  java
  • POJ 3977:Subset(折半枚举+二分)

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

    【题目大意】

      在n个数(n<36)中选取一些数,使得其和的绝对值最小.

    【题解】

      因为枚举所有数选或者不选,复杂度太高无法承受,
      我们考虑减小枚举的范围,我们将前一半进行枚举,保存其子集和,
      然后后一半枚举子集和取反在前一半中寻找最接近的,两部分相加用以更新答案。

    【代码】

    #include <cstdio>
    #include <utility>
    #include <algorithm>
    #include <map>
    using namespace std;
    typedef long long LL;
    const int N=40; 
    int n; LL a[N];
    LL Abs(LL x){return x<0?-x:x;}
    int main(){
        while(scanf("%d",&n)&&n){
            for(int i=0;i<n;i++)scanf("%lld",&a[i]);
            map<LL,int> M;
            map<LL,int>::iterator it;
            pair<LL,int> ans(Abs(a[0]),1);
            for(int i=1;i<1<<(n/2);i++){
                LL s=0; int cnt=0;
                for(int j=0;j*2<n;j++){if((i>>j)&1)s+=a[j],cnt++;}
                ans=min(ans,make_pair(Abs(s),cnt));
                if(M[s])M[s]=min(M[s],cnt);
                else M[s]=cnt;
            }
            for(int i=1;i<1<<(n-n/2);i++){
                LL s=0; int cnt=0;
                for(int j=0;j<(n-n/2);j++){
                    if((i>>j)&1)s+=a[j+n/2],cnt++;
                }ans=min(ans,make_pair(Abs(s),cnt));
                it=M.lower_bound(-s);
                if(it!=M.end())ans=min(ans,make_pair(Abs(s+it->first),cnt+it->second));
                if(it!=M.begin()){
                    it--;
                    ans=min(ans,make_pair(Abs(s+it->first),cnt+it->second));
                }
            }printf("%lld %d
    ",ans.first,ans.second);
        }return 0;
    }
    

      

  • 相关阅读:
    2019 SDN第六次作业
    第07组 Beta冲刺(2/4)
    第07组 Beta冲刺(1/4)
    2019 SDN第5次作业
    SDN课程阅读作业(2)
    第08组 Beta版本演示
    第08组 Beta冲刺(4/4)
    第08组 Beta冲刺(3/4)
    第08组 Beta冲刺(2/4)
    第08组 Beta冲刺(1/4)
  • 原文地址:https://www.cnblogs.com/forever97/p/poj3977.html
Copyright © 2011-2022 走看看