解题报告

前置知识:折半查找法(二分法)

顾名思义,折半就是把一组数据(有序)分成两半,判断我们要找的key值在哪一半当中,不断重复该操作直至找到目标key值,这玩意说白了就是二分的另一个名字。

解决

一说二分大家都知道,但问题在于我们怎么往二分那里去想。首先打眼一看这道题不像一般的思维题那样有明显的规律,事实上也的确没有,那么我们初步的思路就只能是暴力枚举。

但单纯的暴力肯定是GG的,N<=35就意味着我们复杂度最多会达到2^35 这个级别,显然不现实。那么怎么减少枚举的次数呢,我们肯定会想到二分?那么为什么二分是可行的呢,我们如果把35个元素看作一个集合,然后把这个集合分成尽量均等的两份(为使复杂度尽量小,下面会说到),所以问题转化为从一个集合中找到若干集合(元素)使几个元素的绝对值最小,转变成求两个集合中的若干集合(元素)所组成的集合的绝对值最小。这里我们设这两个集合为A和B,简单表示一下就是:

  • | 原集合中选若干个元素组成的子集 | = | A中选若干个元素 | + | B中选若干个元素 |

如果A中有numA个元素,那么A中的预选方案就有2^numA 种,这里可以用状态压缩的思想来解释。对于每一个元素只有选与不选两种状态,我们用一个二进制串来表示,1代表选0代表不选,那么总状态很显然有2^numA 种,B也是是一样的。

这样我们就可以对区间进行枚举了,使用整数表示集合,将所有可能的和与相对应的元素个数存入map,同时每遍历一个组合,就比较其是否比当前结果更优,如果是则更新结果。

然后对第二个区间进行枚举,每枚举出一种组合的和sum,比较更新结果。然后使用map内置的lower_bound函数,在第一个区间里找到比−sum大的最小元素,判断该元素与sum相加是否能构成更好的解。此时不能忘了,我们还要看比−sum小的最大元素,同样判断这种情况是否能构成更好的解。

我们是否需要对前半个区间取空集或者后半个区间取空集进行特判呢?好像没有关注过的样子?答案是不需要管它。因为我们无论是在前后区间进行枚举的时候,一旦找到一个sum值,就会判断它的绝对值是否比当前最优解更小,如果此时更新了结果值,也就是说我们只在单个区间里取了这些个元素,另一个区间根本没有枚举。已经考虑了空的情况。

实现

自己的代码有点小问题。。。先放上一个正解的,自己的等考完了再改改。。。(记得每次要清空map)

#include<iostream>
#include<iomanip>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>
#include<string.h>
#include<math.h>
using namespace std; #define ll long long
#define inf 1e9
#define MAX 100000
#define pair pair<ll,ll>
#define abs(x) ((x)>=0?(x):-(x))//手写的取绝对值函数,虽然不知道为什么但必须手写 ll n, a[40];
pair ans;
map<ll, ll > p;//value->len
map<ll, ll>::iterator it;//迭代器,都多久没用过了... void solve() {
//折半枚举
for (ll i = 1; i < (1 << (n / 2)); i++) {//枚举前(n/2)位
ll t = i, sum = 0, len = 0;
for (ll j = n / 2 - 1; j >= 0; j--) {
if (t&(1 << j)) { sum += a[j]; len++; }
}
ll tmp = abs(sum);
if (tmp < ans.first || (tmp == ans.first&&len < ans.second))
ans = make_pair(tmp, len);
p[sum] > 0 ? p[sum] = min(p[sum], len) : p[sum] = len;
}
for (ll i = 1; i < (1 << (n - n / 2)); i++) {//枚举后(n/2)位 考虑空集
ll t = i, sum = 0, len = 0;
for (ll j = n - 1; j >= n / 2; j--) {
ll v = j - n / 2;
if (t&(1 << v)) { sum += a[j]; len++; }
}
ll tmp = abs(sum);
if (tmp < ans.first || (tmp == ans.first&&len < ans.second))
ans = make_pair(tmp, len);
it = p.lower_bound(-sum);//才知道map还自带lower_bound
if (it != p.end()) {//这是一个最接近他的大于它的相反数的元素
ll val = abs((*it).first + sum), l = (*it).second + len;
if (ans.first > val || (ans.first == val && l < ans.second)) {
ans.first = val; ans.second = l;
}
}
if (it != p.begin()) {//这是一个最接近他的小于它的相反数的元素
it--;
ll val = abs((*it).first + sum), l = (*it).second + len;
if (ans.first > val || (ans.first == val && l < ans.second)) {
ans.first = val; ans.second = l;
}
}
}
cout << ans.first << " " << ans.second << endl;
return;
} int main(){
ios::sync_with_stdio(0);
while (cin >> n) {
if (n == 0) return 0;
p.clear();//每次清map
for (ll i = 0; i < n; i++) { cin >> a[i];}
ans = make_pair(abs(a[0]), 1);
solve();
}
}

[POJ3977] Subet(二分枚举)的更多相关文章

  1. FZU-2216 The Longest Straight (二分枚举)

    题目大意:给n个0~m之间的数,如果是0,那么0可以变为任意的一个1~m之间的一个数.从中选出若干个数,使构成一个连续的序列.问能构成的最长序列的长度为多少? 题目分析:枚举连续序列的起点,二分枚举二 ...

  2. uva 12587 二分枚举

    思路:维护一个森林,二分枚举最小的最大值. #include<set> #include<map> #include<cmath> #include<queu ...

  3. SDIBT 3237 Boring Counting( 划分树+二分枚举 )

    http://acm.sdibt.edu.cn/JudgeOnline/problem.php?id=3237 Problem H:Boring Counting Time Limit: 3 Sec  ...

  4. POJ 3273 Monthly Expense 二分枚举

    题目:http://poj.org/problem?id=3273 二分枚举,据说是经典题,看了题解才做的,暂时还没有完全理解.. #include <stdio.h> #include ...

  5. POJ 2112 Optimal Milking(Floyd+多重匹配+二分枚举)

    题意:有K台挤奶机,C头奶牛,每个挤奶机每天只能为M头奶牛服务,下面给的K+C的矩阵,是形容相互之间的距离,求出来走最远的那头奶牛要走多远   输入数据: 第一行三个数 K, C, M  接下来是   ...

  6. hdu 5248 序列变换(二分枚举)

    Problem Description 给定序列A={A1,A2,...,An}, 要求改变序列A中的某些元素,形成一个严格单调的序列B(严格单调的定义为:Bi<Bi+,≤i<N). 我们 ...

  7. HDU 1669 Jamie's Contact Groups(多重匹配+二分枚举)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1669 题目大意: 给你各个人可以属于的组,把这些人分组,使这些组中人数最多的组人数最少,并输出这个人数 ...

  8. Codeforces 807C - Success Rate(二分枚举)

    题目链接:http://codeforces.com/problemset/problem/807/C 题目大意:给你T组数据,每组有x,y,p,q四个数,x/y是你当前提交正确率,让你求出最少需要再 ...

  9. Codeforces 801C Voltage Keepsake(二分枚举+浮点(模板))

    题目链接:http://codeforces.com/contest/801/problem/C 题目大意:给你一些电器以及他们的功率,还有一个功率一定的充电器可以给这些电器中的任意一个充电,并且不计 ...

  10. BFS+状态压缩DP+二分枚举+TSP

    http://acm.hdu.edu.cn/showproblem.php?pid=3681 Prison Break Time Limit: 5000/2000 MS (Java/Others)   ...

随机推荐

  1. 一个Redis查询案例

    1.远程登陆进服务器 使用ssh连接至Linux服务器中 2.接入redis集群 redis-cli -h 10.1.8.12 -p 29000 3.执行查询命令 根据userid查询用户的最近在线时 ...

  2. 基于Nginx实现访问控制、连接限制

    0 前言 Nginx自带的模块支持对并发请求数进行限制, 还有对请求来源进行限制.可以用来防止DDOS攻击.阅读本文须知道nginx的配置文件结构和语法. 1. 默认配置语法 nginx.conf作为 ...

  3. Linux: 如何分割文件,不再被 4G 大小限制了

    单文件 4G 限制 FAT32 4G 限制 百度网盘超出 4G 限制 单文件分割与合并 单文件,如:archive.tar.gz 分割 split -b 3000M -d -a 1 archive.t ...

  4. Sublime 配置 Markdown,并实时预览

    准备: 找到菜单栏:Preferences → Package Control → Package Control:Install Package 需要安装的插件: [Markdown Editing ...

  5. C语言视频教程下载(百万年薪程序员录制,免费公开)

    <C/C++语言高性能服务开发基础>您可以自由下载.传播.发布或其它商业用途. 视频文件共13.6G,提供了QQ群文件和百度网盘两种方法,建议采用QQ群文件下载,速度较快. 一.下载方法 ...

  6. <VCC笔记> 推断操作符,映射和量词

    推断操作符 在VCC中,==>符号意味着逻辑推理结果,即离散数学中的蕴涵关系.P==>Q等价于((!P)||(Q)).是非常常用的操作符. 量词(quantifier) 关于量词,这里指的 ...

  7. 【JMeter_20】JMeter逻辑控制器__事务控制器<Transaction Controller>

    事务控制器<Transaction Controller> 业务逻辑: 这个控制器在在业务控制上并没有什么特殊逻辑,可以理解为在简单控制器的基础上添加了统计的功能,当所有子节点全部成功则成 ...

  8. WeChair项目Alpha冲刺(6/10)

    团队项目进行情况 1.昨日进展    Alpha冲刺第六天 昨日进展: 前端:和后端成功交互,页面修改和完善 后端:和前端成功交互,但是数据解密失败,初步编写登录的service层和dao层代码未测试 ...

  9. 3.WebPack配置文件

    一.为什么需要WebPack配置文件 引用自官方: 在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件.这比在终端( ...

  10. leetcode1028 从先序遍历还原二叉树 python 100%内存 一次遍历

    1028. 从先序遍历还原二叉树 python 100%内存 一次遍历     题目 我们从二叉树的根节点 root 开始进行深度优先搜索. 在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是 ...