poj 3977 子集

Posted shuaihui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 3977 子集相关的知识,希望对你有一定的参考价值。

题目

题意:在一个集合中找到一个非空子集使得这个子集元素和的绝对值尽量小,和绝对值相同时保证元素个数尽量小

分析:1.二分枚举的思想,先分成两个集合;

       2.枚举其中一个集合中所有的子集并且存到数组中,并排序;

      3.枚举另一个集合中所有的子集并且与第一个集合中的合适子集相加(可以通过二分查找在数组中找到最合

适的元素)

  4.这个题特别坑的地方是不能用abs库函数,只能手写

AC代码:

技术分享图片
#include<stdio.h>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
ll a[50];
ll Abs(ll x)
{
    return x<0?-x:x;
}
int main( )
{
  int n;
  while(scanf("%d",&n)!=EOF)
  {    
      if(n==0)
        break;
      for(int i=0 ; i<n ; i++)
       scanf("%lld",&a[i]);
      int n2=n/2;
      pair<ll,int> ans(Abs(a[0]),1);
      map<ll,int>M;
      map<ll,int>::iterator it;
      for(int i=1 ; i<1<<n2 ; i++)
      {
          ll sum=0;int cnt=0;
          for(int j=0 ; j<n2 ; j++)
          {
              if(i>>j&1)
              {
                  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-n2) ; i++)
      {
          ll sum=0;int cnt=0;
          for(int j=0 ; j<n-n2 ; j++)
          {
              if(i>>j&1)
              {
                  sum+=a[j+n2];
                  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;

}
View Code

带解析

技术分享图片
#include <iostream>
#include <algorithm>
#include <limits>
#include <map>
using namespace std;
typedef long long LL;
#define MAX_N 36
LL number[MAX_N];
 
LL ll_abs(const LL& x)    // damn it! error C3861: ‘llabs‘: identifier not found
{
    return x >= 0 ? x : -x;
}
 
///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int N;
    while (cin >> N && N)
    {
        for (int i = 0; i < N; ++i)
        {
            cin >> number[i];
        }
        map<LL, int> dp;    // sum的值<->集合元素个数,这里不是绝对值
        pair<LL, int> result(ll_abs(number[0]), 1);    // 最优解
        for (int i = 0; i < 1 << (N / 2); ++i)    // 枚举前 N / 2
        {
            LL sum = 0;
            int num = 0;
            for (int j = 0; j < N / 2; ++j)
            {
                if ((i >> j) & 1)
                {
                    sum += number[j];
                    ++num;
                }
            }
            if (num == 0)
            {
                continue;
            }
            result = min(result, make_pair(ll_abs(sum), num));
            map<LL, int>::iterator it = dp.find(sum);
            if (it != dp.end())
            {
                it->second = min(it->second, num);
            }
            else
            {
                dp[sum] = num;
            }
        }
 
        for (int i = 0; i < 1 << (N - N / 2); ++i)    // 枚举剩下的
        {
            LL sum = 0;
            int num = 0;
            for (int j = 0; j < N - N / 2; ++j)
            {
                if ((i >> j) & 1)
                {
                    sum += number[N / 2 + j];
                    ++num;
                }
            }
            if (num == 0)
            {
                continue;
            }
            result = min(result, make_pair(ll_abs(sum), num));
            // 找寻跟-sum最相近的结果
            map<LL, int>::iterator it = dp.lower_bound(-sum);    // 返回大于或等于-sum的第一个元素位置
            if (it != dp.end())
            {// 可能是该位置
                result = min(result, make_pair(ll_abs(sum + it->first), num + it->second));
            }
            if (it != dp.begin())
            {// 或比该元素小一点点的
                --it;
                result = min(result, make_pair(ll_abs(sum + it->first), num + it->second));
            }
        }
        cout << ll_abs(result.first) <<   << result.second << endl;
    }
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    system("out.txt");
#endif
    return 0;
}
View Code

 

以上是关于poj 3977 子集的主要内容,如果未能解决你的问题,请参考以下文章

POJ3977 Subset

POJ - 3977 Subset(二分+折半枚举)

POJ3977 Subset

[poj] 3977 Subset || 折半搜索MITM

Subset(POJ-3977)

POJ3977 Subset 折半枚举