题意:

给你一个集合N(N<=35),问集合的子集,除了空集,使得自己中所有元素和的绝对值最小,若存在多个值,那么选择子集中元素最少的那个。

题目:

Given a list of N integers with absolute values no larger than 101510^{15}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

分析:

1.n 最大到 35,每个数有选、不选两种可能,最多有 2352^{35}235 个子集,因此暴力枚举的话,一定会Time Limit Exceed,采用折半枚举的思想,分成两个集合,这样每边最多 18 个元素,分别进行枚举,复杂度降到 2182^{18}218
2.利用二进制将和以及元素个数存在两个数组中,先预判是否满足题意,再将其中一个元素和取相反数后排序,因为总元素和越接近零越好,再二分查找即可,用lower_bound时考虑查找到的下标和他前一个下标,比较元素和以及元素个数,不断更新即可。
3.由于是求大数的绝对值,此时需要开函数,(不知道为什么,我调用labs函数,结果是错误的,所以自己写了一个)
4.然后枚举其中一个子集,排序后暂存后,再枚举另一个子集,通过二分查找第一个集合中与该值的相反数最接近的元素,要注意的是如果有多个元素与相反值最接近,取数的个数最小的那一个。
5.与寻找合适的子集并与第一个集合的子集相加,从而找到绝对值最小的子集.
下面附上AC代码,里面注解有部分解答。

AC代码:

#include<stdio.h>
#include<map>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int M=50;
int n,cnt=0;
map<ll,ll> vis;
ll Labs(ll x){if(x>0) return x;return -x;
}
ll a[M],dp[1<<20],ans;//vis当前所选的最少个数,dp数组存储前一半的值,ans是最终结果
ll solve(ll num)
{if(num<=dp[1])return dp[1];else if(num>=dp[cnt])return dp[cnt];int mid=upper_bound(dp+1,dp+cnt+1,num)-dp;if(Labs(dp[mid]-num)==Labs(dp[mid-1]-num)){if(vis[dp[mid-1]]<vis[dp[mid]])return dp[mid-1];return dp[mid];}else if(Labs(dp[mid]-num)>Labs(dp[mid-1]-num))return dp[mid-1];return dp[mid];
}//查找与x最接近的元素,要注意的是如果有多个元素与x最接近,取数的个数最小的那一个
int main()
{while(~scanf("%d",&n)&&n){cnt=0,ans=inf;vis.clear();memset(a,0,sizeof(a));memset(dp,0,sizeof(dp));for(int i=1; i<=n; i++)scanf("%lld",&a[i]);if(n==1){printf("%lld 1\n",Labs(a[1]));continue;}int x=n/2,y=n-x;int mi=(1<<x)-1;/*mi为所选元素的所有可能性,除去空集*/ll id=inf;for(int i=1; i<=mi; i++){ll now=0,tot=0;for(int j=1; j<=x; j++){if(i&(1<<(j-1)))/**枚举i为多少种方式,j为多少个数,用j来控制位数,i控制方式*/now+=a[j],tot++;}if(vis[now])vis[now]=min(vis[now],tot);//如果当前值已经出现过,元素个数取较小的elsevis[now]=tot,dp[++cnt]=now;//没有出现过,建立映射关系if(Labs(now)<ans){ans=Labs(now);id=tot;}//如果答案更优,更新答案else if(Labs(now)==ans)id=min(id,tot);//如果答案相同,元素个数取较小的}sort(dp+1,dp+cnt+1);for(int i=1; i<=(1<<y)-1; i++){ll now=0,tot=0;for(int j=1; j<=y; j++)if(i&(1<<(j-1)))now+=a[j+n/2],tot++;if(Labs(now)<ans){ans=Labs(now);id=tot;}else if(Labs(now)==ans)id=min(id,tot);ll num=solve(-now);//二分找到与相反数最接近的数if(ans>Labs(num+now)){ans=Labs(num+now);id=tot+vis[num];}else if(ans==Labs(num+now))id=min(id,tot+vis[num]);}printf("%lld %d\n",ans,id);}return 0;
}

Subset POJ - 3977(折半枚举+二分+二进制枚举)相关推荐

  1. 【暴力枚举】二进制枚举-幼儿园买玩具

    [暴力枚举]二进制枚举-幼儿园买玩具 #include<iostream> #include<cstring> using namespace std; int main(){ ...

  2. poj 1873The Fortified Forest (凸包 二进制枚举)

    注意: 一共n位,所以i从1枚举到 (1< #include <iostream> #include <cmath> #include <cstdio> #i ...

  3. 超大背包问题(二进制枚举 + 二分)

    超大背包问题 第一次看到这一题好像是在某一场比赛,就是给你一个炸空间和时间的背包,让你选最大的价值,看似是01背包然鹅今天在挑战程序设计这本书上看到了这题,看到了作者的做法,感觉豁然开朗,直接暴搜也会 ...

  4. 【HDU 5936 --- Difference】折半枚举+二分

    [HDU 5936 --- Difference]折半枚举+二分 Description Little Ruins is playing a number game, first he chooses ...

  5. POJ 1873 The Fortified Forest 凸包 二进制枚举

    n最大15,二进制枚举不会超时.枚举不被砍掉的树,然后求凸包 #include<stdio.h> #include<math.h> #include<algorithm& ...

  6. HPU组队赛B:问题(二进制枚举)

    时间限制1 Second 内存限制 512 Mb 题目描述 你有n个问题,你已经估计了第i个问题的难度为Ci,现在你想使用这些问题去构造一个问题集.比赛的问题集必须包含至少两个问题,而且比赛的总难度必 ...

  7. BZOJ1688|二进制枚举子集| 状态压缩DP

    Disease Manangement 疾病管理 Description Alas! A set of D (1 <= D <= 15) diseases (numbered 1..D) ...

  8. 116. 飞行员兄弟【二进制枚举】

    二进制枚举即可,需要注意的是,这道题和费解的开关还是有不同点的. 费解的开关枚举第一行就行了,因为它有依赖关系的. 而这个是一个十字形,无那种关系.故直接暴力枚举邓按不按的所有情况即可. #inclu ...

  9. 1362. 健康的荷斯坦奶牛【难度: 一般 / 二进制枚举】

    https://www.acwing.com/problem/content/1364/ 二进制枚举即可,取一个最小值. #include<bits/stdc++.h> using nam ...

最新文章

  1. EF 调试跟踪生成的SQL语句
  2. 查询linux kafka安装目录,Linux下安装并(单节点)配置启动Kafka
  3. RxJava2.x 萌新之路 操作符篇
  4. vue+element 后台管理系统(三)树形图
  5. qq邮箱电脑版登录入口_Windows版TIM体验更新,微信可以直接登录QQ
  6. PIC中档单片机汇编指令详解(6)
  7. jQuery学习笔记--目录
  8. 音视频技术开发周刊 | 176
  9. geek_Ask How-To Geek:营救受感染的PC,安装无膨胀iTunes和驯服疯狂的触控板
  10. java前期_【JAVA】前期环境配置
  11. python内存地址替换原理(20秒读懂)
  12. atitit.软件开发--socket框架选型--netty vs mina j
  13. 下棋计算机是什么配置的啊,一个下棋人的电脑配置,请大家指教
  14. Mac WebStorm 破解
  15. Dorado7之AjaxAction
  16. 模拟CMOS集成电路放大器总结(1)
  17. 知识树 = 系统思维 + 外接大脑
  18. 华为路由器AAA配置与管理
  19. Leetcode1160. 拼写单词(C语言)
  20. 张量、向量、标量的区别

热门文章

  1. 剑指offer之判断二叉树是不是平衡二叉树
  2. Android之通过ActivityLifecycleCallbacks判断程序是否运行在后台
  3. 汇编语言之数据处理的2个基本问题
  4. Android之Lollipop DevicePolicyManager学习(上)
  5. Android之如何解决popupWindow(pw.setFocusable(true))按返回键和menu键退出
  6. 学习socket nio 之 mina实例
  7. 1小时零基础赚一千,教你完成图书管理系统,不用打代码绝对学得会![完整全站教学 IVX 实战第四篇]
  8. 计算机绘画作品 星空,关于近期绘画作品《星空系列》的自述:
  9. java爬虫工具xpath提取,2020-07-16--爬虫数据提取--xpath
  10. COMA(二):Counterfactual Multi-Agent Policy Gradients 论文讲解