题目大意

给定N(N<=35)个数字,每个数字都<= 2^15. 其中一个或多个数字加和可以得到s,求出s的绝对值的最小值,并给出当s取绝对值最小值时,需要加和的数字的个数。

题目分析

需要枚举集合的所有情况,2^35,会超时。考虑使用折半枚举的方法,考虑前 N/2个数字构成的集合S1,在S1中进行所有情况枚举,复杂度为 2^17,并将所有可能的和sum以及构成和sum需要的数字个数count存放在map M中;然后在S2中进行所有情况的枚举,复杂度为2^17,对于每种情况的sum2,在M中查找 -sum2的位置,在该位置前后位置处进行查找,求和的最小值。 
    还需要考虑,当s只有S1中的数字构成或者s只有S2中的数字构成,或者s由S1和S2中的数字构成的三类情况。 
    总的时间复杂度为 O(2^17 + 2^17*log(2^17)) = O(2^22)

实现(c++)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<cmath>
#include<iostream>
#include<map>
using namespace std; long long int ll_abs(long long int n){
if (n >= 0)
return n;
return -n;
}
long long int an[40]; map<long long int, int> sum_map;
int main2(){
int n;
while (scanf("%d", &n) && n){
map<long long int, int>::iterator it; sum_map.clear();
for (int i = 0; i < n; i++){
scanf("%lld", &an[i]);
}
long long int min_sum = ll_abs(an[0]);
int min_count = 1; int m = n / 2;
for (int i = 0; m > 0 && i < (1 << m); i++){
long long int sum = 0;
int count = 0;
int t = i;
for (int k = 0; k < m; k++){
if (t & 1){
sum += an[k];
count++;
}
t >>= 1;
}
if (count == 0)
continue; if (sum_map.find(sum) != sum_map.end()){
sum_map[sum] = min(sum_map[sum], count);
}
else
sum_map[sum] = count;
if (ll_abs(sum) < min_sum){
min_sum = ll_abs(sum);
min_count = count;
}
else if (ll_abs(sum) == min_sum){
min_count = min(min_count, count);
}
}
m = n / 2 + n % 2;
for (int i = 0; i < (1 << m); i++){
long long int sum = 0;
int count = 0;
int t = i;
for (int k = 0; k < m; k++){
if (t & 1){
sum += an[n / 2 + k];
count++;
}
t >>= 1;
}
if (count == 0)
continue; if (ll_abs(sum) < min_sum){
min_sum = ll_abs(sum);
min_count = count;
}
else if (ll_abs(sum) == min_sum){
min_count = min(min_count, count);
} it = sum_map.lower_bound(-sum);
if (it != sum_map.end()){
long long int s = sum + it->first;
if (ll_abs(s) < min_sum){
min_sum = ll_abs(s);
min_count = it->second + count;
}
else if (ll_abs(s) == min_sum){
min_count = min(min_count, it->second + count);
}
}
if (it != sum_map.begin()){
--it;
long long int s = sum + it->first;
if (ll_abs(s) < min_sum){
min_sum = ll_abs(s);
min_count = it->second + count;
}
else if (ll_abs(s) == min_sum){
min_count = min(min_count, it->second + count);
}
}
} printf("%lld %d\n", min_sum, min_count);
}
return 0;
}

poj_3977 折半枚举的更多相关文章

  1. Load Balancing 折半枚举大法好啊

    Load Balancing 给出每个学生的学分.   将学生按学分分成四组,使得sigma (sumi-n/4)最小.         算法:   折半枚举 #include <iostrea ...

  2. CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。

    1514: Packs Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 61  Solved: 4[Submit][Status][Web Board] ...

  3. NYOJ 1091 超大01背包(折半枚举)

    这道题乍一看是普通的01背包,最最基础的,但是仔细一看数据,发现普通的根本没法做,仔细观察数组发现n比较小,利用这个特点将它划分为前半部分和后半部分这样就好了,当时在网上找题解,找不到,后来在挑战程序 ...

  4. Codeforces 888E - Maximum Subsequence(折半枚举(meet-in-the-middle))

    888E - Maximum Subsequence 思路:折半枚举. 代码: #include<bits/stdc++.h> using namespace std; #define l ...

  5. Codeforces 912 E.Prime Gift (折半枚举、二分)

    题目链接:Prime Gift 题意: 给出了n(1<=n<=16)个互不相同的质数pi(2<=pi<=100),现在要求第k大个约数全在所给质数集的数.(保证这个数不超过1e ...

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

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

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

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

  8. CodeForces888E Maximum Subsequence(折半枚举+two-pointers)

    题意 给定一个包含\(n\)个数的序列\(a\),在其中任选若干个数,使得他们的和对\(m\)取模后最大.(\(n\leq 35\)) 题解 显然,\(2^n\)的暴枚是不现实的...,于是我们想到了 ...

  9. 【折半枚举】Ural Championship April 30, 2017 Problem G. Glasses with solutions

    题意:有n杯盐溶液,给定每杯里面盐的质量以及盐溶液的质量.问你有多少种方案选择一个子集,使得集合里面的盐溶液倒到一个被子里面以后,浓度为A/B. 折半枚举,暴力搜索分界线一侧的答案数,跨越分界线的答案 ...

随机推荐

  1. 【C#】List<T>对象的深复制

    一.List对象中的T是值类型的情况(int 类型等) 对于值类型的List直接用以下方法就可以复制: List<T> oldList = new List<T>(); old ...

  2. MySQL基本命令和操作

    开启和关闭mysql服务 Windows下开启和关闭mysql服务 net start mysql # 开启mysql服务 net stop mysql # 关闭mysql服务 没有重启mysql服务 ...

  3. eclipse 里的tomcat一直处于starting状态

    今天困扰了我一天,eclipse里面明明吧tomcat启动成功了,但是有个starting tomcat的进程一直在启动着,当你修改页面,build workspace的时候,进程就一直处于等待状态, ...

  4. wireshark教程(一)

    按照国际惯例,从最基本的说起. 抓取报文: 下载和安装好Wireshark之后,启动Wireshark并且在接口列表中选择接口名,然后开始在此接口上抓包.例如,如果想要在无线网络上抓取流量,点击无线接 ...

  5. Win7/Win8下双击执行jar程序的方法

    问题 老妈喜欢看小说,又不会下载,于是用Java写了个自己主动下载小说的小程序 我用的Swing UI.直接生成了.jar文件,双击就可以执行 非常诡异的是.用Eclipse直接run能够执行,可是E ...

  6. 【C】——回调函数实现泛型算法

    回调函数的一个典型应用就是实现类似C++的泛型算法(Generics Algorithm).下面实现的max函数可以在任意一组对象中找出最大值,可以是一组int.一组char或者一组结构体,但是实现者 ...

  7. 等待事件 wait event

    衡量数据库状况, 优化数据库等 当一个进程连接到数据库后, 进程所经历的种种等待就开始被记录, 并且通过一系列的性能视图进行展示, 通过等待事件用户可以很快发现数据库的性能瓶颈, 从而进行针对性能的优 ...

  8. Java处理图片时编译不通过

    Java中处理图片时,MyEclipse需要导入以下包: import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.j ...

  9. MySQl安装全解

    这是第二次安装MySql了.第一次安装花了几个小时,理解安装的每一个页面,这次光寻找安装包就找了几个.因此感觉有必要做一次全面的安装笔记.(有点浪费时间了,可是感觉非常值得)本人系统是window7. ...

  10. R语言低级绘图函数-rect

    rect 函数用来在一张图上添加矩形,只需要指定左下角和右上角的坐标的位置,就可以画出一个矩形 基本用法: plot(1:5, 1:5, xlim = c(0,6), ylim = c (0,6), ...