POJ3977 Subset
嘟嘟嘟
这个数据范围显然是折半搜索。
把序列分成两半,枚举前一半的子集,存下来。然后再枚举后一半的子集,二分查找。
细节:
1.最优解可能只在一半的子集里,所以枚举的时候也要更新答案。
2.对于当前结果\(tot\),二分查找\(-tot\)的时候要把\(-tot\)两边的元素都和\(tot\)加起来试一下,而不是只加当前二分查找到的值。
3.用二进制枚举比较快。
4.得去重,即和相同,保留元素个数最小的集合。(扫一遍即可)
5.更新的时候元素个数别忘了是两部分之和,刚开始我因为这个没写对拍了好一会儿(而且小数据还都过了……)。
6.\(INF\)要开到\(1e18\),因为数据范围是\(a _ i \leqslant 1e15\)。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const ll INF = 1e18;
const db eps = 1e-8;
const int maxn = 36;
const int maxp = 3e5 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, m, q, cnt = 0;
ll a[maxn];
struct Node
{
ll sum; int num;
bool operator < (const Node& oth)const
{
return sum < oth.sum || (sum == oth.sum && num < oth.num);
}
}t[maxp], s[maxp];
ll Min = INF;
int ans;
ll Abs(ll x) {return x < 0 ? -x : x;}
int main()
{
while(scanf("%d", &n) && n)
{
m = n >> 1; q = n - m; cnt = 0;
Min = INF; ans = 1;
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i) Min = min(Min, Abs(a[i]));
for(int i = 1; i < (1 << m); ++i)
{
ll tot = 0; int tcnt = 0;
for(int j = 0; j < m; ++j)
if((1 << j) & i) tot += a[j + 1], tcnt++;
if(Abs(tot) < Min) Min = Abs(tot), ans = tcnt;
else if(Abs(tot) == Min) ans = min(ans, tcnt);
t[++cnt] = (Node){tot, tcnt};
}
sort(t + 1, t + cnt + 1);
int scnt = 0, x = 1;
for(int i = 2; i <= cnt; ++i)
{
if(t[i].sum != t[x].sum) s[++scnt] = t[x], x = i;
else t[x].num = min(t[x].num, t[i].num);
}
if(t[x].sum != s[scnt].sum) s[++scnt] = t[x];
for(int i = 1; i < (1 << q); ++i)
{
ll tot = 0; int tcnt = 0;
for(int j = 0; j < q; ++j)
if((1 << j) & i) tot += a[j + m + 1], tcnt++;
if(Abs(tot) < Min) Min = Abs(tot), ans = tcnt;
else if(Abs(tot) == Min) ans = min(ans, tcnt);
int pos = lower_bound(s + 1, s + scnt + 1, (Node){-tot, 0}) - s;
if(pos && pos <= scnt)
{
ll tp = Abs(tot + s[pos].sum);
if(tp < Min) Min = tp, ans = s[pos].num + tcnt;
else if(tp == Min) ans = min(ans, s[pos].num + tcnt);
}
if(pos - 1 > 0 && pos - 1 <= scnt)
{
ll tp = Abs(tot + s[pos - 1].sum);
if(tp < Min) Min = tp, ans = s[pos - 1].num + tcnt;
else if(tp == Min) ans = min(ans, s[pos - 1].num + tcnt);
}
}
write(Min), space, write(ans), enter;
}
return 0;
}
POJ3977 Subset的更多相关文章
- poj3977 - subset - the second time - 暴力 + 二分
2017-08-26 11:38:42 writer:pprp 已经是第二次写这个题了,但是还是出了很多毛病 先给出AC代码: 解题思路: 之前在培训的时候只是笼统的讲了讲怎么做,进行二分对其中一边进 ...
- POJ3977 Subset 折半枚举
题目大意是给定N个数的集合,从这个集合中找到一个非空子集,使得该子集元素和的绝对值最小.假设有多个答案,输出元素个数最少的那个. N最多为35,假设直接枚举显然是不行的. 可是假设我们将这些数分成两半 ...
- poj 折半搜索
poj2549 Sumsets 题目链接: http://poj.org/problem?id=2549 题意:给你一个含有n(n<=1000)个数的数列,问这个数列中是否存在四个不同的数a,b ...
- POJ3977:Subset——题解(三分+折半搜索)
http://poj.org/problem?id=3977 题目大意:有一堆数,取出一些数,记他们和的绝对值为w,取的个数为n,求在w最小的情况下,n最小,并输出w,n. ————————————— ...
- [LeetCode] Partition Equal Subset Sum 相同子集和分割
Given a non-empty array containing only positive integers, find if the array can be partitioned into ...
- [LeetCode] Largest Divisible Subset 最大可整除的子集合
Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of ...
- 洛谷 P1466 集合 Subset Sums Label:DP
题目描述 对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的.举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,每个子 ...
- LeetCode "Largest Divisible Subset" !
Very nice DP problem. The key fact of a mutual-divisible subset: if a new number n, is divisible wit ...
- 【USACO 2.2】Subset Sums (DP)
N (1 <= N <= 39),问有多少种把1到N划分为两个集合的方法使得两个集合的和相等. 如果总和为奇数,那么就是0种划分方案.否则用dp做. dp[i][j]表示前 i 个数划分到 ...
随机推荐
- js 实现复制到粘贴板功能
前言:js 或者 jquery 都可以实现的复制到粘贴板功能,有时还想要有换行等格式(同 textarea) 网站地址:我的个人vue+element ui demo网站 github地址:yuleG ...
- ef和mysql使用(二)--让mysql支持EntityFramework.Extended实现批量更新和删除
我们都知道Entity Framework 中不能同时更新多条记录,但是一个老外写的扩展库可以实现此功能EntityFramework.Extended,但是如何是mysql数据库要怎么实现呢 首先实 ...
- canvas框架::createjs入门
createjs是一个轻量级的框架,稍微有点时间和耐心,就可以把全部源代码都看一遍,毕竟只有三十几个js文件.地址:http://www.createjs.com/ 开发createjs的动画或游戏, ...
- 使用99元一年的256MB高性能阿里云Redis加速Discuz论坛
介绍 Discuz是一个常见的论坛,支持使用Redis来对论坛进行加速访问,对于访问量比较大的论坛能够取到很好的作用,本文介绍如何使用阿里云高性价比256MBRedis来加速该论坛. 阿里云Redis ...
- RollViewPager图片轮播效果开源框架的使用
RollViewPager是一个自动轮播的Viewpager, 支持无限循环. 触摸时会暂停播放,直到结束触摸一个延迟周期以后继续播放. 看起来就像这样.指示器可以为点可以为数字还可以自定义,位置也可 ...
- @Secured()、 @PreAuthorize() 、 @RolesAllowed()
在Spring security的使用中,为了对方法进行权限控制,通常采用的三个注解,就是@Secured().@PreAuthorize().@RolesAllowed(). 示例,修改用户密码必须 ...
- mac下连接阿里云总是提示密码是吧,permission denied
早上使用mac连接阿里云服务器 ,总是提示 连接拒绝 之前还是好好的,密码自己有没有改过... 搞了半天,是 没输入用户名.... 上图中 输入 用户 然后输入密码就行了.
- 基于 Docker 的现代软件供应链
[编者按]本文作者为 Marc Holmes,主要介绍一项关于现代软件供应链的调查结果.本文系国内 ITOM 管理平台 OneAPM 编译呈现,以下为正文. 3 月初,为了了解软件供应链的现状以及 D ...
- scrapy实战--登陆人人网爬取个人信息
今天把scrapy的文档研究了一下,感觉有点手痒,就写点东西留点念想吧,也做为备忘录.随意写写,看到的朋友觉得不好,不要喷我哈. 创建scrapy工程 cd C:\Spider_dev\app\scr ...
- 全局唯一ID生成器
分布式环境中,如何保证生成的id是唯一不重复的? twitter,开源出了一个snowflake算法,现在很多企业都按照该算法作为参照,实现了自己的一套id生成器. 该算法的主要思路为: 刚好64位的 ...