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 个数划分到 ...
随机推荐
- 【转】Apache服务器的下载与安装
PHP的运行必然少不了服务器的支持,何为服务器?通俗讲就是在一台计算机上,安装个服务器软件,这台计算机便可以称之为服务器,服务器软件和计算机本身的操作系统是两码事,计算机自身的操作系统可以为linux ...
- HDU 3501 Calculation 2------欧拉函数变形
Calculation 2 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- js 实现复制到粘贴板功能
前言:js 或者 jquery 都可以实现的复制到粘贴板功能,有时还想要有换行等格式(同 textarea) 网站地址:我的个人vue+element ui demo网站 github地址:yuleG ...
- Var与Dynamic的区别
1.var与dynamic的区别 C#中的很多关键词用法比较容易混淆,var和dynamic就是其中一组,但其实它们是有本质的区别的.var 在编译阶段已经确定类型,在初始化时候,必须提供初始化的 ...
- css 画图形大全
Square #square { width: 100px; height: 100px; background: red; } Rectangle #rectangle { width: 2 ...
- 应用ArcGIS Server JavaScript API实现地图卷帘效果实现
var maskDynamicMapServiceLayer = null; var maskDynamicMapServiceLayerDiv = null; var pointNumb = 0; ...
- 八、Vue中的computed属性
看了网上很多资料,对vue的computed讲解自己看的都不是很清晰,今天忙里抽闲,和同事们又闲聊起来,对computed这个属性才有了一个稍微比较清晰的认识,下面的文章有一部分是转自: https: ...
- asp.net 一般处理程序实现网站验证码
使用VerifyCode.ashx一般处理程序生成验证码,实现如下: using System; using System.Drawing; using System.Web; using Syste ...
- [UI] 精美UI界面欣赏[5]
精美UI界面欣赏[5]
- python2.7下同步华为云照片的爬虫程序实现
1.背景 随着华为手机的销量加大,华为云的捆绑服务使用量也越来越广泛,华为云支持自动同步照片.通讯录.记事本等,用着确实也挺方便的,云服务带来方便的同时,也带来了数据管理风险.华为目前只提供一个www ...