嘟嘟嘟




这个数据范围显然是折半搜索。

把序列分成两半,枚举前一半的子集,存下来。然后再枚举后一半的子集,二分查找。

细节:

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的更多相关文章

  1. poj3977 - subset - the second time - 暴力 + 二分

    2017-08-26 11:38:42 writer:pprp 已经是第二次写这个题了,但是还是出了很多毛病 先给出AC代码: 解题思路: 之前在培训的时候只是笼统的讲了讲怎么做,进行二分对其中一边进 ...

  2. POJ3977 Subset 折半枚举

    题目大意是给定N个数的集合,从这个集合中找到一个非空子集,使得该子集元素和的绝对值最小.假设有多个答案,输出元素个数最少的那个. N最多为35,假设直接枚举显然是不行的. 可是假设我们将这些数分成两半 ...

  3. poj 折半搜索

    poj2549 Sumsets 题目链接: http://poj.org/problem?id=2549 题意:给你一个含有n(n<=1000)个数的数列,问这个数列中是否存在四个不同的数a,b ...

  4. POJ3977:Subset——题解(三分+折半搜索)

    http://poj.org/problem?id=3977 题目大意:有一堆数,取出一些数,记他们和的绝对值为w,取的个数为n,求在w最小的情况下,n最小,并输出w,n. ————————————— ...

  5. [LeetCode] Partition Equal Subset Sum 相同子集和分割

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  6. [LeetCode] Largest Divisible Subset 最大可整除的子集合

    Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of ...

  7. 洛谷 P1466 集合 Subset Sums Label:DP

    题目描述 对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的.举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,每个子 ...

  8. LeetCode "Largest Divisible Subset" !

    Very nice DP problem. The key fact of a mutual-divisible subset: if a new number n, is divisible wit ...

  9. 【USACO 2.2】Subset Sums (DP)

    N (1 <= N <= 39),问有多少种把1到N划分为两个集合的方法使得两个集合的和相等. 如果总和为奇数,那么就是0种划分方案.否则用dp做. dp[i][j]表示前 i 个数划分到 ...

随机推荐

  1. [HTML5] Canvas绘制简单形状

    使用canvas来进行绘画,它像很多其他dom对象一样,有很多属性和方法,操作这些方法,实现绘画 获取canvas对象,调用document.getElementById()方法 调用canvas对象 ...

  2. Spring界的HelloWorld

    1.Spring依赖的jar包下载网址: https://repo.spring.io/webapp/#/artifacts/browse/tree/General/libs-release-loca ...

  3. 响应式布局和BootStrap 全局CSS样式

    1.什么是响应式布局 响应式布局是Ethan Marcotte在2010年5月份提出的一个概念,这个概念是为解决移动互联网浏览而诞生的. 简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一 ...

  4. span与a元素的键盘聚焦性以及键盘点击性研究——张鑫旭

    一.众所周知的与不为所知的 我们平时涉及点击交互事件的时候,都是使用的a元素或者button元素,原因是可以相应键盘focus效果以及回车触发点击事件,这是众所周知的. 但是,可能存在一些特殊情况,我 ...

  5. struts2 上传下载文件,异常处理,数据类型转换

    一,web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version=" ...

  6. div实现返回符,倒三角,椭圆+小知识收集

    收集: 1,返回符(伪类元素): .back:before {content: "";width: .3rem;height: .3rem;border-left: .04rem ...

  7. html基础-表格-列表(4)

    今天准备为大家准备了表格和列表. 一.文章有各种数据的表格这个网页也不例外. (1).标签意思 <table>----------------------表格开始 <caption& ...

  8. 什么是URI,URL以及URN,你真的理解了吗。

    先举一个例子,让大家对这三个名词又一个基本的概念: 1⃣️ ftp://ftp.is.co.za/rfc/rfc1808.txt 2⃣️ http://www.cnblogs.com/nods/p/8 ...

  9. CSS深入理解之absolute(HTML/CSS)

    absolute和float是同父异母的兄弟,因为它们具有相同点:包裹性与破坏性 absolute的特点 1.独立的,并且可以摆脱overflow的限制,无论是滚动还是隐藏: 2.无依赖,不受rela ...

  10. pjax 和 ajax 的区别

    pjax 是一个 jQuery 插件,它通过 ajax 和 pushState 技术提供了极速的(无刷新 ajax 加载)浏览体验,并且保持了真实的地址.网页标题,浏览器的后退(前进)按钮也可以正常使 ...