Subset
Time Limit: 30000MS   Memory Limit: 65536K
Total Submissions: 3161   Accepted: 564

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

Source

 
 
 
解析:折半枚举。N个元素的集合,子集有2N个,除去空集,有2N-1个子集。而N可达35,235-1这个数就很大了,即使给了30000ms,直接枚举也会超时,可以考虑折半枚举。把集合分为两部分,这样两个小集合元素规模至多为18,枚举量为218,在可以承受的范围内。这样就得到了两个可枚举的集合A和B,最终的结果来源于这三种情况:子集的元素只取自于A、子集的元素只取自于B、子集的元素取自A和B。对于子集的元素只取自于A、子集的元素只取自于B这两种情况,我们在枚举的时候不断更新就可以了。对于子集的元素取自A和B这种情况,我们在枚举B(假设得到的子集中所有元素的和为sum)时,最佳情况为在A中找到-sum,这样我们在二分查找时,在-sum附近进行更新即可。另外,POJ暂不支持64位的abs,自己写一个吧^_^
 
 
 
#include <cstdio>
#include <map>
#define ll long long
using namespace std; int n;
ll a[40]; ll ll_abs(ll x)
{
return x >= 0 ? x : -x;
} void solve()
{
map<ll, int> mp;
map<ll, int>::iterator it;
pair<ll, int> res(ll_abs(a[0]), 1); //初始化结果为第一个元素
for(int i = 1; i < 1<<(n/2); ++i){ //枚举区间为[1, 2^n),当i为0时,子集为空
ll sum = 0;
int num = 0;
for(int j = 0; j < n/2; ++j){ //按位枚举
if((i>>j)&1){
sum += a[j];
++num;
}
}
res = min(res, make_pair(ll_abs(sum), num)); //子集的元素只取自于A
it = mp.find(sum);
if(it != mp.end())
it->second = min(it->second, num);
else
mp[sum] = num;
}
for(int i = 1; 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 += a[n/2+j];
++num;
}
}
res = min(res, make_pair(ll_abs(sum), num)); //子集的元素只取自于B
it = mp.lower_bound(-sum); //查找与-sum最相近的值
if(it != mp.end()) //可能在该位置
res = min(res, make_pair(ll_abs(it->first+sum), it->second+num));
if(it != mp.begin()){ //可能在该位置的前一个位置
--it;
res = min(res, make_pair(ll_abs(it->first+sum), it->second+num));
}
}
printf("%I64d %d\n", res.first, res.second);
} int main()
{
while(scanf("%d", &n), n){
for(int i = 0; i < n; ++i)
scanf("%I64d", &a[i]);
solve();
}
return 0;
}

  

  

POJ 3977 Subset的更多相关文章

  1. POJ 3977 - subset - 折半枚举

    2017-08-01 21:45:19 writer:pprp 题目: • POJ 3977• 给定n个数,求一个子集(非空)• 使得子集内元素和的绝对值最小• n ≤ 35 AC代码如下:(难点:枚 ...

  2. poj 3977 Subset(折半枚举+二进制枚举+二分)

    Subset Time Limit: 30000MS   Memory Limit: 65536K Total Submissions: 5721   Accepted: 1083 Descripti ...

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

    SubsetTime Limit: 30000MS        Memory Limit: 65536KTotal Submissions: 6754        Accepted: 1277 D ...

  4. 【折半枚举+二分】POJ 3977 Subset

    题目内容 Vjudge链接 给你\(n\)个数,求出这\(n\)个数的一个非空子集,使子集中的数加和的绝对值最小,在此基础上子集中元素的个数应最小. 输入格式 输入含多组数据,每组数据有两行,第一行是 ...

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

    原题 给定N个整数组成的数列(N<=35),从中选出一个子集,使得这个子集的所有元素的值的和的绝对值最小,如果有多组数据满足的话,选择子集元素最少的那个. n<=35,所以双向dfs的O( ...

  6. POJ 3977 Subset | 折半搜索

    题目: 给出一个整数集合,求出非空子集中元素和绝对值最小是多少(元素个数尽量少) 题解: 分成两半 爆搜每一半,用map维护前一半的值 每搜出后一半的一个值就去map里找和他和绝对值最小的更新答案 # ...

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

    题意:有一个N(N <= 35)个数的集合,每个数的绝对值小于等于1015,找一个非空子集,使该子集中所有元素的和的绝对值最小,若有多个,则输出个数最小的那个. 分析: 1.将集合中的元素分成两 ...

  8. POJ 3977:Subset(折半枚举+二分)

    [题目链接] http://poj.org/problem?id=3977 [题目大意] 在n个数(n<36)中选取一些数,使得其和的绝对值最小. [题解] 因为枚举所有数选或者不选,复杂度太高 ...

  9. Subset POJ - 3977(折半枚举+二分查找)

    题目描述 Given a list of N integers with absolute values no larger than 10 15, find a non empty subset o ...

随机推荐

  1. [转载]Spring Autowire自动装配介绍

    转自: http://www.cnblogs.com/zhishan/p/3190757.html 在应用中,我们常常使用<ref>标签为JavaBean注入它依赖的对象.但是对于一个大型 ...

  2. SPOJ LCS 后缀自动机

    用后缀自动机求两个长串的最长公共子串,效果拔群.多样例的时候memset要去掉. 解题思路就是跟CLJ的一模一样啦. #pragma warning(disable:4996) #include< ...

  3. HDU 1882 Strange Billboard(位运算)

    题目链接 题意 : 给你一个矩阵,有黑有白,翻转一个块可以让上下左右都翻转过来,问最少翻转多少次能让矩阵变为全白. 思路 : 我们从第一行开始枚举要翻转的状态,最多可以枚举到2的16次方,因为你只要第 ...

  4. SDUT1586 计算组合数(组合数)

    这个题数据量小,不容易超时. #include<stdio.h> long long fac(int n) { ; ; i <= n ; i++) { m = i*m; } retu ...

  5. DJANGO中,用QJUERY的AJAX的json返回中文乱码的解决办法

    和网上其它用JAVA或是PHP的实现不太一样, DJANGO中的解决办法如下: 后端样例: def render_to_json_response(context, **response_kwargs ...

  6. 再谈PCA

        其实之前写过PCA相关的博文,但是由于之前掌握的理论知识有限,所以理解也比较浅.这篇博文,我们以另外一种角度来理解PCA看,这里我假设大家对PCA都有一个初步的了解.首先,我们举一个二维空间中 ...

  7. Go语言的优点(oschina讨论)

    Go语言的优点:并发/网络/性能/工具(fmt/pprof/test)/标准库(http/json/log/flags/atomic)/GoogleGo语言垃圾回收器真正致命的缺陷是,会导致整个进程不 ...

  8. C#基础精华----枚举

    enums枚举是值类型,数据直接存储在栈中,而不是使用引用和真实数据的隔离方式来存储. (1)默认情况下,枚举中的第一个变量被赋值为0,其他的变量的值按定义的顺序来递增(0,12,3...),因此以下 ...

  9. Hibernate 主键策略

    Hibernate主键生成策略 .自动增长identity 适用于MySQL.DB2.MS SQL Server,采用数据库生成的主键,用于为long.short.int类型生成唯一标识 使用SQL ...

  10. PHP无限极分类生成树方法

    你还在用浪费时间又浪费内存的递归遍历无限极分类吗,看了该篇文章,我觉得你应该换换了.这是我在OSChina上看到的一段非常精简的PHP无限极分类生成树方法,整理分享了. function genera ...