【折半枚举+二分】POJ 3977 Subset
题目内容
Vjudge链接
给你\(n\)个数,求出这\(n\)个数的一个非空子集,使子集中的数加和的绝对值最小,在此基础上子集中元素的个数应最小。
输入格式
输入含多组数据,每组数据有两行,第一行是元素组合\(n\)(若\(n\)为0表示输入结束),第二行有\(n\)个数,表示要给出的\(n\)个数。
数据范围
\(n\le 35\)
输出格式
每组数据输出一行两个数中间用空格隔开,表示最小的绝对值和该子集的元素个数。
样例输入
1
10
3
20 100 -100
0
样例输出
10 1
0 2
思路
在学必修一的第一节课我们就知道有\(n\)个数的集合的非空子集有\(2^n-1\)个,因此此题直接枚举的话大小回到\(2^{35}-1\),显然出题人没有这么好心。
这里用到一种思想叫做对半枚举。可以想到\(2^{17}-1=131071\),比较优秀的,那么就开始对半枚举就好了。将\(n\)的数分为前\(\frac{n}{2}\)和后\(\frac{n}{2}\)进行枚举。同时可以结合二分查找。
如果是一个空集加上一个非空,那么得到的也是非空集合,当然如果两个空集加在一起肯定也还是空集。
注意下边界。
代码
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
ll Abs(ll x){//据说POJ不支持abs函数?
    return x > 0 ? x : -x;
}
int n;
ll a[40];
int main(){
    while (scanf("%d",&n)&&n!=0){
        for (int i=1;i<=n;++i)
            scanf("%d",a+i);
        map<ll,int> g;//一开始想用结构体结果发现结构体不能用lower_bound
        ll ans=Abs(a[1]);
        int len=n;
        for (int i=1;i<(1<<(n/2));++i){
            ll sum=0;
            int j=i,cnt=0,pos=1;
            while(j&&pos<=n/2){
                if (j&1){
                    sum+=a[pos];
                    cnt++;
                }
                j>>= 1;
                pos++;
            }
            if (Abs(sum)<ans){
                ans=Abs(sum);
                len=cnt;
            }
            else if(Abs(sum)==ans){
                len=min(len, cnt);
            }
            if(g[sum])
                g[sum]=min(g[sum], cnt);
            else
                g[sum]=cnt;
        }
        for (int i=1;i<(1<<(n-n/2));++i){
            ll sum=0;
            int cnt=0,pos=1,j=i;
            while (j&&pos+n/2<= n){
                if (j&1){
                    sum+= a[pos+n/2];
                    cnt++;
                }
                j>>= 1;
                pos++;
            }
            if(Abs(sum) < ans){
                ans=Abs(sum);
                len=cnt;
            }
            else if(Abs(sum)==ans){
                len=min(len,cnt);
            }
            map<ll,int>::iterator it=g.lower_bound(-sum);//迭代器都要忘光了orz
            if(it!=g.end()){
                if(Abs(sum+it->first)<ans){
                    ans=Abs(sum+it->first);
                    len=cnt+it->second;
                }
                else if(Abs(sum+it->first)==ans){
                    len=min(len,cnt+it->second);
                }
            }
            if (it!=g.begin()){
                it--;
                if(Abs(sum+it->first)<ans){
                    ans=Abs(sum+it->first);
                    len=cnt+it->second;
                }
                else if(Abs(sum+it->first)==ans){
                    len=min(len,cnt+it->second);
                }
            }
        }
        scanf("%d %d\n",Abs(ans),len);
    }
    return 0;
}
代码启发
https://www.jianshu.com/p/27eefa7b990e
膜一下dalao的玛丽
【折半枚举+二分】POJ 3977 Subset的更多相关文章
- CSU OJ PID=1514: Packs   超大背包问题,折半枚举+二分查找。
		
1514: Packs Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 61 Solved: 4[Submit][Status][Web Board] ...
 - poj 3977 Subset(折半枚举+二进制枚举+二分)
		
Subset Time Limit: 30000MS Memory Limit: 65536K Total Submissions: 5721 Accepted: 1083 Descripti ...
 - POJ  3977 Subset(折半枚举+二分)
		
SubsetTime Limit: 30000MS Memory Limit: 65536KTotal Submissions: 6754 Accepted: 1277 D ...
 - POJ 3977 - subset - 折半枚举
		
2017-08-01 21:45:19 writer:pprp 题目: • POJ 3977• 给定n个数,求一个子集(非空)• 使得子集内元素和的绝对值最小• n ≤ 35 AC代码如下:(难点:枚 ...
 - POJ 3977 Subset
		
Subset Time Limit: 30000MS Memory Limit: 65536K Total Submissions: 3161 Accepted: 564 Descriptio ...
 - POJ 3977:Subset(折半枚举+二分)
		
[题目链接] http://poj.org/problem?id=3977 [题目大意] 在n个数(n<36)中选取一些数,使得其和的绝对值最小. [题解] 因为枚举所有数选或者不选,复杂度太高 ...
 - Subset POJ - 3977(折半枚举+二分查找)
		
题目描述 Given a list of N integers with absolute values no larger than 10 15, find a non empty subset o ...
 - POJ - 3977 Subset(二分+折半枚举)
		
题意:有一个N(N <= 35)个数的集合,每个数的绝对值小于等于1015,找一个非空子集,使该子集中所有元素的和的绝对值最小,若有多个,则输出个数最小的那个. 分析: 1.将集合中的元素分成两 ...
 - POJ 2549 Sumsets(折半枚举+二分)
		
Sumsets Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11946 Accepted: 3299 Descript ...
 
随机推荐
- Java使用JDBC连接Oracle数据库
			
import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; /** * @author liu ...
 - spring boot+spring security集成以及Druid数据库连接池的问题
			
贴工程目录,其中bll目录下是service+dao层,common是一些公用的模块及功能类,web是controller层 用到了druid及Redis,工具及配置类目录(本文不介绍如何配置drui ...
 - linux下部署python项目到jenkins
			
环境:linux+jenkins+tomcat+git+python3.7 1.安装jdk 上传安装包到usr/local 解压 配置环境变量 vim /etc/profile export JAVA ...
 - 第一课、python基础学习笔记
			
自动化非自动化的区别 自动化测试就是让机器按照人的想法把功能全部跑一遍 自动化测试的过程,让我们写一段程序去测试另一段程序是否正常的过程 Java 编译型语言, 编码-->编译-->解 ...
 - wsl 修改默认安装路径
			
如果已经装了,先删除 mklink /j C:\Users\XXXX\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rh ...
 - Docker Swarm 集群环境搭建及弹性服务部署
			
上一篇文章<Docker Swarm 集群管理利器核心概念扫盲>中我们把 Swarm 重要的概念性知识给大家讲解了一波,理论完事就该实战了,这篇文章带大家从零开始,搭建 Docker Sw ...
 - 高可用集群之keepalived+lvs实战2
			
keepalived简介 lvs在我之前的博客<高负载集群实战之lvs负载均衡-技术流ken>中已经进行了详细的介绍和应用,在这里就不再赘述.这篇博文将把lvs与keepalived相结合 ...
 - 7.kafka HA
 - [De1CTF 2019]Giftbox 分析&&TPOP学习
			
[De1CTF 2019]Giftbox 刚进来我以为是直接给了shell,恐怖如斯. 随便扔了个命令,之后就没然后了,hhh,截包发现可能存在sql注入. 然后我就不会了... what i lea ...
 - Echarts山东省地图两级钻取、返回及济南合并莱芜地图
			
Echarts3.0+jQuery3.3.1 山东省地图中济南市需要注意下,莱芜市已经和济南市合并,地图数据来源于地图选择器,获取山东省地图信息及各地市地图信息(JSON格式) //山东地图(第一级地 ...