嘟嘟嘟




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

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

细节:

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. 【转】Apache服务器的下载与安装

    PHP的运行必然少不了服务器的支持,何为服务器?通俗讲就是在一台计算机上,安装个服务器软件,这台计算机便可以称之为服务器,服务器软件和计算机本身的操作系统是两码事,计算机自身的操作系统可以为linux ...

  2. HDU 3501 Calculation 2------欧拉函数变形

    Calculation 2 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  3. js 实现复制到粘贴板功能

    前言:js 或者 jquery 都可以实现的复制到粘贴板功能,有时还想要有换行等格式(同 textarea) 网站地址:我的个人vue+element ui demo网站 github地址:yuleG ...

  4. Var与Dynamic的区别

    1.var与dynamic的区别   C#中的很多关键词用法比较容易混淆,var和dynamic就是其中一组,但其实它们是有本质的区别的.var 在编译阶段已经确定类型,在初始化时候,必须提供初始化的 ...

  5. css 画图形大全

    Square   #square { width: 100px; height: 100px; background: red; } Rectangle   #rectangle { width: 2 ...

  6. 应用ArcGIS Server JavaScript API实现地图卷帘效果实现

    var maskDynamicMapServiceLayer = null; var maskDynamicMapServiceLayerDiv = null; var pointNumb = 0; ...

  7. 八、Vue中的computed属性

    看了网上很多资料,对vue的computed讲解自己看的都不是很清晰,今天忙里抽闲,和同事们又闲聊起来,对computed这个属性才有了一个稍微比较清晰的认识,下面的文章有一部分是转自: https: ...

  8. asp.net 一般处理程序实现网站验证码

    使用VerifyCode.ashx一般处理程序生成验证码,实现如下: using System; using System.Drawing; using System.Web; using Syste ...

  9. [UI] 精美UI界面欣赏[5]

    精美UI界面欣赏[5]

  10. python2.7下同步华为云照片的爬虫程序实现

    1.背景 随着华为手机的销量加大,华为云的捆绑服务使用量也越来越广泛,华为云支持自动同步照片.通讯录.记事本等,用着确实也挺方便的,云服务带来方便的同时,也带来了数据管理风险.华为目前只提供一个www ...