POJ 3977 解题报告

Subset

Time Limit: 30000MS Memory Limit: 65536K
Total Submissions: 6499 Accepted: 1227

Description

Given a list of N integers with absolute values no larger than 1015, find a non empty subset of these numbers which minimizes the absolute value of the sum of its elements. In case there are multiple subsets, choose the one with fewer elements.

Input

The input contains multiple data sets, the first line of each data set contains N <= 35, the number of elements, the next line contains N numbers no larger than 1015 in absolute value and separated by a single space. The input is terminated with N = 0

Output

For each data set in the input print two integers, the minimum absolute sum and the number of elements in the optimal subset.

Sample Input

1
10
3
20 100 -100
0

Sample Output

10 1
0 2

 

题目的意思 你可以取非空集合 任意组合 看看能不能绝对值最小 也就是越接近0

我们怎么看这题 我们先想 这有35个东西

又是组合 很容易想到二进制枚举 但是2^35太大了 

那么我们又为了能做题 想到2^17是个比较好的范围

那么我们就可以对半枚举了

把前n/2个和后n/2个分开 进行枚举

从i=0到(1<<n/2)-1(可以取) 为什么从0开始呢?

因为我们可以是一个空集 加 一个非空集 组合 也是一个非空集合

那我们马上发现 两个空集合加起来肯定是空集合

所以如果枚举的两个集合都是空集 那么得到的也是空集

所以我们可以进行二进制枚举了

前面写了一发 不知道为啥WA 目前还不知道

重新写了就过了。。。

lower_bound不能直接对结构体 所以开一个新数组

并且在找到的xb左右范围进行二分 防止迷之二分

注意判断下边界条件

 

/*
POJ 3977
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
#define dbg(x) ;
ll arr[45];
ll ans_new[1<<18];
ll ABS(ll a){
   if(a>=0) return a;
   return -a;
}
struct node {
  ll sum;
  int num;
  bool operator < (const node other) const
  {
  if(sum==other.sum) return num < other.num;
  return sum<other.sum;
  }
}ans_l[1<<18],ans_r[1<<18];
int main(){
   int n;
   while(scanf("%d",&n)&&n){
    for(int i= 1;i<=n;++i)  scanf("%lld",&arr[i]);
        int tmp_l = n/2;
        int tmp_r = n-tmp_l;
        for(int i = 0;i<(1<<tmp_l);i++){
            ans_l[i].sum = 0;
            int cnt = 1,cnt_now = 0,now_i = i;
            while(now_i){
                if(now_i&1){
                    cnt_now ++;
                    ans_l[i].sum += arr[cnt];
                }
                cnt++;
                now_i/=2;
            }
            ans_l[i].num = cnt_now;
        }
        for(int i = 0;i<(1<<tmp_r);i++){
                ans_r[i].sum = 0;
            int cnt = 1,cnt_now = 0,now_i = i;
            while(now_i){
                if(now_i&1){
                    cnt_now ++;
                    ans_r[i].sum += arr[cnt+tmp_l];
                }
                cnt++;
                now_i/=2;
            }
            ans_r[i].num = cnt_now;
            ans_new[i] = ans_r[i].sum;
        }
        ll maxx = INF;
        int num = 0;
      sort(ans_r,ans_r+(1<<tmp_r));
      sort(ans_new,ans_new+(1<<tmp_r));
      for(int i = 0 ;i<(1<<tmp_l);i++){
        int xb = lower_bound(ans_new,ans_new+(1<<tmp_r),-ans_l[i].sum)-ans_new;
        for(int j = xb-1;j<=xb+1;j++){
            if(j<0||j>=(1<<tmp_r)) continue;
            if(ans_r[j].num==0&&ans_l[i].num==0) continue;
            if(maxx>ABS(ans_new[j]+ans_l[i].sum)){
                maxx = ABS(ans_new[j]+ans_l[i].sum);
                num = ans_r[j].num+ans_l[i].num;
                dbg(ans_r[j].num);
                dbg(ans_l[i].num);
                dbg(ans_new[j]);
                dbg(ans_l[i].sum);
                dbg(num);
                dbg(maxx);
            }
            else if(maxx == ABS(ans_new[j]+ans_l[i].sum)){
                if(num> ans_r[j].num+ans_l[i].num) num = ans_r[j].num+ ans_l[i].num;
            }
        }
      }
      printf("%lld %d\n",maxx,num);
   }

   return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值