Divide and conquer:Subset(POJ 3977)

题目大意:给定一串数字序列,要你从中挑一定个数的数字使这些数字和绝对值最小,求出最小组合数
题目的数字最多35个,一看就是要数字枚举了,但是如果直接枚举,复杂度就是O(2^35)了,显然行不通,所以我们把它的组合拆成两半(前n/2个数字和后n-n/2个数字),然后给前部分和或者后部分和的组合排序,然后再用另一半在其二分查找,看最接近的和,这就是折半枚举的思想
不过这一题有很多细节:
1.和是不固定的,要左右各查找1个,如果有重复元素,一定要越过重复元素再找(lower_bound和upper_bound找就好了),同时还要防止空子列
2.这一题要找的数组合数最小的和个组合,所以排序的那一个组合,当两个组合的和一样时,要按照nums的升序排列,这样也会让1的查找顺利
#include <iostream>
#include <algorithm>
#include <functional> using namespace std; typedef long long LL_INT;
static struct _set
{
int nums;
LL_INT sum;
bool operator <(const _set&x) const
{
if (sum != x.sum)
return sum < x.sum;
else
return nums < x.nums;//如果相等则按nums排序,那样只用看upper_bound就可以了
}
}sum_set1[], sum_set2[];
static LL_INT input[];
static int search_bound[] = { -, }; LL_INT ABS(LL_INT);
int Min(const int, const int);
void Solve(const int, LL_INT &, int &);
struct _set *Binary_Search_Lower(const int, LL_INT);
struct _set *Binary_Search_Upper(const int, LL_INT); int main(void)
{
int ans2, n;
LL_INT ans1; while (~scanf("%d",&n))
{
if (n == )break;
for (int i = ; i < n; i++)
scanf("%lld", &input[i]); for (int i = ; i < << (n / ); i++)//枚举左半边元素
{
sum_set1[i].nums = ; sum_set1[i].sum = ;
for (int pos = ; pos < n / ; pos++)
{
if (((i >> pos) & ) == )
{
sum_set1[i].sum += input[pos];
sum_set1[i].nums++;
}
}
}
for (int i = ; i < <<(n - (n / )); i++)//枚举右半边元素
{
sum_set2[i].nums = ; sum_set2[i].sum = ;
for (int pos = ; pos < (n - (n / )); pos++)
{
if (((i >> pos) & ) == )
{
sum_set2[i].sum += input[pos + n / ];
sum_set2[i].nums++;
}
}
}
sort(sum_set2, sum_set2 + ( << (n - (n / ))));
Solve(n, ans1, ans2);
printf("%lld %d\n", ans1, ans2);
}
return EXIT_SUCCESS;
} LL_INT ABS(LL_INT x)
{
return x > ? x : -x;
} int Min(const int x, const int y)
{
return x > y ? y : x;
} void Solve(const int n, LL_INT &ans1, int &ans2)
{
struct _set *pos = NULL, *pos_up = NULL;
int tmp_pos, up;
ans1 = LLONG_MAX; ans2 = -; for (int i = ; i < << (n / ); i++)
{
pos = Binary_Search_Lower( << (n - (n / )), -sum_set1[i].sum);
pos_up = Binary_Search_Upper( << (n - (n / )), -sum_set1[i].sum);
//一定要记得在找到的范围附近再寻找看还有没有绝对值更接近的
for (int j = ; j < ; j++)
{
tmp_pos = (int)(pos - sum_set2) + search_bound[j];
if ( <= tmp_pos && tmp_pos < << (n - (n / ))
&& (sum_set2[tmp_pos].nums || i)//不同时为0(避免空子串)
)
{
if (ABS(sum_set2[tmp_pos].sum + sum_set1[i].sum) < ans1)
{
ans1 = ABS(sum_set2[tmp_pos].sum + sum_set1[i].sum);
ans2 = sum_set2[tmp_pos].nums + sum_set1[i].nums;
}
else if (ABS(sum_set2[tmp_pos].sum + sum_set1[i].sum) == ans1)
ans2 = Min(sum_set2[tmp_pos].nums + sum_set1[i].nums, ans2);
}
}
up = (int)(pos - sum_set2) + ;
if (sum_set2[up].nums || i)//避免空子串,导致后面失效
{
if (ABS(sum_set2[up].sum + sum_set1[i].sum) < ans1)
{
ans1 = ABS(sum_set2[up].sum + sum_set1[i].sum);
ans2 = sum_set2[up].nums + sum_set1[i].nums;
}
else if (ABS(sum_set2[up].sum + sum_set1[i].sum) == ans1)
ans2 = Min(sum_set2[up].nums + sum_set1[i].nums, ans2);
}
up = (int)(pos_up - sum_set2);
if (sum_set2[up].nums || i)//不同时为0(避免空子串)
{
if (ABS(sum_set2[up].sum + sum_set1[i].sum) < ans1)
{
ans1 = ABS(sum_set2[up].sum + sum_set1[i].sum);
ans2 = sum_set2[up].nums + sum_set1[i].nums;
}
else if (ABS(sum_set2[up].sum + sum_set1[i].sum) == ans1)
ans2 = Min(sum_set2[up].nums + sum_set1[i].nums, ans2);
}
}
} struct _set *Binary_Search_Lower(const int n, LL_INT sum1)
{
int lb = , mid, count1 = n, count2;
while (count1 > )
{
count2 = count1 >> ;
mid = lb + (count1 >> );
if (sum_set2[mid].sum < sum1)
{
lb = ++mid;
count1 -= count2 + ;
}
else count1 = count2;
}
return &sum_set2[lb];
} struct _set *Binary_Search_Upper(const int n, LL_INT sum1)
{
int lb = , mid, count1 = n, count2;
while (count1 > )
{
count2 = count1 >> ;
mid = lb + (count1 >> );
if (sum_set2[mid].sum <= sum1)
{
lb = ++mid;
count1 -= count2 + ;
}
else count1 = count2;
}
return &sum_set2[lb];
}

最后尼玛,我wa很多次,发现原来是我的min函数写错了。。。写成了max函数。。。。。
参考了一下http://www.cnblogs.com/hyxsolitude/p/3642053.html
Divide and conquer:Subset(POJ 3977)的更多相关文章
- Subset POJ - 3977(折半枚举+二分查找)
题目描述 Given a list of N integers with absolute values no larger than 10 15, find a non empty subset o ...
- Divide and conquer:Sumsets(POJ 2549)
数集 题目大意:给定一些数的集合,要你求出集合中满足a+b+c=d的最大的d(每个数只能用一次) 这题有两种解法, 第一种就是对分,把a+b的和先求出来,然后再枚举d-c,枚举的时候输入按照降序搜索就 ...
- Divide and conquer:Showstopper(POJ 3484)
Showstopper 题目大意:数据挖掘是一项很困难的事情,现在要你在一大堆数据中找出某个数重复奇数次的数(有且仅有一个),而且要你找出重复的次数. 其实我一开始是没读懂题意的...主要是我理解错o ...
- Divide and conquer:Garland(POJ 1759)
挂彩灯 题目大意:就是要布场的时候需要挂彩灯,彩灯挂的高度满足: H1 = A Hi = (Hi-1 + Hi+1)/2 - 1, for all 1 < i < N HN = B Hi ...
- Divide and conquer:Matrix(POJ 3685)
矩阵 题目大意:矩阵里面的元素按i*i + 100000 * i + j*j - 100000 * j + i*j填充(i是行,j是列),求最小的M个数 这一题要用到两次二分,实在是二分法的经典,主要 ...
- Divide and conquer:Median(POJ 3579)
快速求两数距离的中值 题目大意:给你一个很大的数组,要你求两个数之间的距离的中值 二分法常规题,一个pos位就搞定的事情 #include <iostream> #include ...
- Divide and conquer:Drying(POJ 3104)
烘干衣服 题目大意:主人公有一个烘干机,但是一次只能烘干一件衣服,每分钟失水k个单位的水量,自然烘干每分钟失水1个单位的水量(在烘干机不算自然烘干的那一个单位的水量),问你最少需要多长时间烘干衣服? ...
- POJ 3977 - subset - 折半枚举
2017-08-01 21:45:19 writer:pprp 题目: • POJ 3977• 给定n个数,求一个子集(非空)• 使得子集内元素和的绝对值最小• n ≤ 35 AC代码如下:(难点:枚 ...
- [LeetCode] 236. Lowest Common Ancestor of a Binary Tree_ Medium tag: DFS, Divide and conquer
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...
随机推荐
- 简单实用的PHP防注入类实例
这篇文章主要介绍了简单实用的PHP防注入类实例,以两个简单的防注入类为例介绍了PHP防注入的原理与技巧,对网站安全建设来说非常具有实用价值,需要的朋友可以参考下 本文实例讲述了简单实用的PHP防注 ...
- HTTP Basic Authentication
Client端发送请求, 要在发送请求的时候添加HTTP Basic Authentication认证信息到请求中,有两种方法:1. 在请求头中添加Authorization: Authoriz ...
- 【CISP笔记】数据库及应用安全
数据库安全特性检查工具美国应用安全公司的App Detective英国下一代软件公司的NGS SQuirrel 常见应用安全威胁 网络层面拒绝服务.电子欺骗.嗅探.……系统层面Web服务漏洞.配置错误 ...
- seo是什么职业
SEO(Search Engine Optimization)汉译为搜索引擎优化.seo从业者首要工作就是优化网站,使其符合搜索引擎的基本规律和满足用户的需求,进而获得大量的用户访问.SEO职业属于一 ...
- [译]git pull
git pull把git fetch和git merge压缩成了一条命令. 用法 git pull <remote> 作用和git fetch <remote> &&a ...
- 提交上了,却在iTunes Connect没有新版本的任何消息
上架的时候,收到这样的邮件 This app attempts to access privacy-sensitive data without a usage description. The ap ...
- phpMyAdmin 缺少 mysqli 扩展。请检查 PHP 配置
好久没有在windows下配置php了,今天,按照前一篇文章配置好,打开phpmyadmin时,出现如下问题: phpMyAdmin 缺少 mysqli 扩展.请检查 PHP 配置 网上搜索解决方案, ...
- gc是什么,什么时候需要gc
Java是由C++发展来的. 它摈弃了C++中一些繁琐容易出错的东西.其中有一条就是这个GC. 写C/C++程序,程序员定义了一个变量,就是在内存中开辟了一段相应的空间来存值.内存再大也是有限的,所以 ...
- android socket编程用Bufferreader读取的一个失败教训
由于我的手机需要用笔记本开的wifi,躺在床上玩手机时需要关电脑或者是要让电脑放歌的时候总是不想下床,于是我想能不能用一个APP,然后通过局域网实现在手机上对电脑进行操控呢?说干就干. 我在电脑上用的 ...
- jQuery常用操作方法及常用函数总结
一篇 jQuery 常用方法及函数的文章留存备忘. jQuery 常见操作实现方式 $("标签名") //取html元素 document.getElementsByTagName ...