题目大意是给定N个数的集合,从这个集合中找到一个非空子集,使得该子集元素和的绝对值最小。假设有多个答案,输出元素个数最少的那个。

N最多为35,假设直接枚举显然是不行的。

可是假设我们将这些数分成两半后再枚举的话,最多有2^18(262144),此时我们两半枚举后的结果进行排序后再二分搜索一下就能够了。复杂度为O(nlogn)
n最多2^18。

#include <stdio.h>
#include <vector>
#include <math.h>
#include <string.h>
#include <string>
#include <iostream>
#include <queue>
#include <list>
#include <algorithm>
#include <stack>
#include <map> using namespace std; struct MyStruct
{
long long res;
int i;
}; int compp(const void* a1, const void* a2)
{
long long dif = ((MyStruct*)a1)->res - ((MyStruct*)a2)->res;
if (dif > 0)
{
return 1;
}
else if (dif == 0)
{
return 0;
}
else
return -1;
} MyStruct res[2][300000]; inline long long absll(long long X)
{
if (X < 0)
{
return X * (-1);
}
else
return X;
} int main()
{
int n;
#ifdef _DEBUG
freopen("d:\\in.txt", "r", stdin);
#endif
long long values[36];
while (scanf("%d", &n) != EOF)
{
if (n == 0)
{
break;
}
for (int i = 0; i < n; i++)
{
scanf("%I64d", &values[i]);
}
int maxn = n - n / 2;
int maxm = n - maxn;
memset(res, 0, sizeof(res));
for (int i = 0; i < 1 << maxn; i++)
{
res[0][i].i = i;
for (int k = 0; k < 19; k++)
{
if ((i >> k) & 1)
{
res[0][i].res += values[k];
}
}
}
qsort(res[0], 1 << maxn, sizeof(MyStruct), compp);
for (int i = 0; i < 1 << maxm; i++)
{
res[1][i].i = i;
for (int k = 0; k < 19; k++)
{
if ((i >> k) & 1)
{
res[1][i].res += values[k + maxn];
}
}
}
qsort(res[1], 1 << maxm, sizeof(MyStruct), compp);
long long minvalue = 1000000000000000LL;
int mink = 32;
int l = 0;
int r = (1 << maxm);
for (int i = 0; i < 1 << maxn; i++)
{
l = 0;
int curk = 0;
for (int k = 0; k < maxn; k++)
{
if ((res[0][i].i >> k) & 1)
{
curk++;
}
}
while (r - l > 1)
{
int mid = (l + r) / 2;
long long sum = res[1][mid].res + res[0][i].res;
if (sum > 0)
{
r = mid;
}
else
l = mid;
} l = l >= 1 ? l - 1 : l;
for (int k = l; k < (1 << maxm);k++)
{
int curm = 0;
for (int m = 0; m < maxm; m++)
{
if ((res[1][k].i >> m) & 1)
{
curm++;
}
}
if (curm == 0 && curk == 0)
{
continue;
}
long long sum = res[1][k].res + res[0][i].res;
if (absll(sum) < minvalue)
{
mink = curm + curk;
minvalue = absll(sum);
}
else if (absll(sum) == minvalue)
{
mink = min(mink, curk + curm);
}
else if (sum > 0)
{
break;
}
}
}
printf("%I64d %d\n", minvalue, mink);
}
return 0;
}

POJ3977 Subset 折半枚举的更多相关文章

  1. POJ 3977 Subset(折半枚举+二分)

    SubsetTime Limit: 30000MS        Memory Limit: 65536KTotal Submissions: 6754        Accepted: 1277 D ...

  2. poj3977(折半枚举+二分查找)

    题目链接:https://vjudge.net/problem/POJ-3977 题意:给一个大小<=35的集合,找一个非空子集合,使得子集合元素和的绝对值最小,如果有多个这样的集合,找元素个数 ...

  3. POJ 3977 - subset - 折半枚举

    2017-08-01 21:45:19 writer:pprp 题目: • POJ 3977• 给定n个数,求一个子集(非空)• 使得子集内元素和的绝对值最小• n ≤ 35 AC代码如下:(难点:枚 ...

  4. poj 3977 Subset(折半枚举+二进制枚举+二分)

    Subset Time Limit: 30000MS   Memory Limit: 65536K Total Submissions: 5721   Accepted: 1083 Descripti ...

  5. 折半枚举——poj3977

    暴力搜索超时,但是折半后两部分状态支持合并的情况,可用折半枚举算法 poj3977 给一个序列a[],从里面找到k个数,使其和的绝对值最小 经典折半枚举法+二分解决,对于前一半数开一个map,map[ ...

  6. Load Balancing 折半枚举大法好啊

    Load Balancing 给出每个学生的学分.   将学生按学分分成四组,使得sigma (sumi-n/4)最小.         算法:   折半枚举 #include <iostrea ...

  7. CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。

    1514: Packs Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 61  Solved: 4[Submit][Status][Web Board] ...

  8. NYOJ 1091 超大01背包(折半枚举)

    这道题乍一看是普通的01背包,最最基础的,但是仔细一看数据,发现普通的根本没法做,仔细观察数组发现n比较小,利用这个特点将它划分为前半部分和后半部分这样就好了,当时在网上找题解,找不到,后来在挑战程序 ...

  9. Codeforces 888E - Maximum Subsequence(折半枚举(meet-in-the-middle))

    888E - Maximum Subsequence 思路:折半枚举. 代码: #include<bits/stdc++.h> using namespace std; #define l ...

随机推荐

  1. 让TortoiseGit记住帐号密码方法

    我的电脑环境是: Windows7 64x   系统用户名是:steden 所以,我的路径是:C:\Users\steden\ 具体要根据你的系统环境及当前用户名来决定. 在这里,有个文件:.gitc ...

  2. 安装gitlab并配置邮箱

    安装要求:运行内存必须大于等于2G 一.安装docker wget -qO- https://get.docker.com/ | sh 镜像加速: echo '{"registry-mirr ...

  3. MySQL系列(一)--数据类型

    如何选择优化的数据类型: 1.通常更小的更好 相同级别的数据类型,选择占据空间更小的数据类型.更小的数据类型通常更快,因为占用更少的磁盘.内存和CPU缓存,处理时需要的 CPU周期也更少,但是要确保需 ...

  4. vue中的main.js打开直接报错问题解决

    安装好后也是报这样的错,是因为WebStorm默认使用的是ES5的语法,改成ES6就可以了.   解决方案:    

  5. spark学习(2)---RDD

    一.打印RDD内容 https://blog.csdn.net/wengyupeng/article/details/52808503 1.方法 2种方式: 1 rdd.collect().forea ...

  6. SDOI2015约数个数和

    题目描述 题解: 有一个式子: 证明先不说了. 然后倒一波反演: 然后整除分块就好了. 代码: #include<cstdio> #include<cstring> #incl ...

  7. 零基础入门学习Python(10)--列表:一个打了激素的数组

    前言 有时候我们需要把一些东西暂时保存起来,因为他们有着一些直接或间接的联系,我们需要把它们放在某个组或者集合中,未来可能用得上. 很多接触过编程的朋友都知道,都接触过数组这个概念,那么数组这个概念事 ...

  8. 零基础入门学习Python(4)--改进我们的小游戏

    前言 在以前的博客中有做个一个小游戏,但是太简单了,所以这次就来对我们做的小游戏进行改进,改善从以下四个方面进行: 程序猜错的时候要给出提示,例如告诉用户输入的值是大了还是小了. 以前程序每运行一次只 ...

  9. awk,grep,sed文本格式化处理

    一.awk取列 [root@web01 ~]# cat /etc/passwd|awk -F ':' '{print $1"\t\t"$7}' ###-F指定分隔符 root /b ...

  10. 【BZOJ 1202】 [HNOI2005]狡猾的商人 (加权并查集)

    题链:http://www.lydsy.com/JudgeOnline/problem.php?id=1202 Description 刁姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪 ...