码迷,mamicode.com
首页 > 其他好文 > 详细

Subset(POJ-3977)

时间:2020-04-25 13:01:03      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:isp   cond   一个   用法   first   air   1的个数   play   判断   

题目描述:

技术图片

 

 Input:

技术图片

 

 Output:

技术图片

 

 技术图片

 

 题意:给定n个数,求出使得总和的绝对值最小的最小集合,该集合是给定的n个数的子集,并输出这个最小总和和集合内的元素个数,在满足总和最小的前提下要求元素个数最小。

思路:这道题如果用暴力来写,由于每个数都有两种情况,故硬枚举就是2^n的效率,但n是小于等于三十五的,这对于指数来说还是太大了。

  本题正解需要用到map,我们借用map来存贮当前情况下每个值下的子集元素个数,用map自带的lower_bound来查找距离指定元素距离最小的值的位置(这两点的具体应用下文会提到)。关于map及lower_bound的详细用法,我向大家推荐这篇博客,这里面有map的实例和用法:

https://blog.csdn.net/yo_bc/article/details/61195377

  下面来说一下这道题的思路:我们先将这个数组分为两部分,先处理前面的一个部分,利用二进制来表示前半部分的每个元素的出现元素,在这里我们要判断一下,如果这个集合的元素个数为0,就直接continue掉,因为题目中说了集合中元素个数不能为0,在得到相应二进制数值后处理一下,计算一下此时的得分总和及元素个数,之后我们判断一下,如果在map之中相应得分已经有了映射,就对map第二位(即相应得分元素个数)取最小值,没有就直接赋值。之后用相同方法处理另一半,最后我们遍历前面一个数组得出的map,在另一半中找到与map中相应位的第一位总合最小的数(用lower_bound查找),并记录此时的两个部分的元素个数之和。之后取最小值即可。

注意开long long。

上代码:

技术图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<map>
 5 typedef long long ll;
 6 using namespace std;
 7 const int N=55;
 8 int n;
 9 ll a[N];
10 map<ll,int>mp;
11 bool read(){  //判断是否输入结束 
12     scanf("%d",&n);
13     if(!n) return false;
14     for(int i=0;i<n;++i){
15         scanf("%lld",&a[i]);
16     }
17     mp.clear();
18     return true;
19 }
20 ll Abs(ll a){  //cmath中的abs不针对long long要自己写一个 
21     return a>0?a:-a;
22 }
23 void Solve(){
24     pair<ll,ll>ans=pair<ll,ll>(Abs(a[0]),1);  //ans记录答案,要先附一个初值,不然滞后没法取min 
25     int mid=(n>>1);
26     map<ll,int>::iterator it;  //迭代器,用于查找元素 
27     int t=(1<<mid);
28     for(int i=0;i<t;++i){  //处理前半个数组 
29         ll sum=0;int num=0;
30         for(int j=0;j<mid;++j)  //统计二进制中1的个数 
31             if((i>>j)&1){
32                 sum+=a[j];num++;
33             }
34         if(!num) continue;  //没有元素直接continue 
35         ans=min(ans,pair<ll,ll>(Abs(sum),num)); //计算ans 
36         it=mp.find(sum);  //查找 
37         if(it!=mp.end())  //如果原来有取min,原来没有直接赋值 
38             it->second=min(it->second,num);
39         else mp[sum]=num;
40     }
41     t=(1<<(n-mid));
42     for(int i=0;i<t;++i){  //后半部分 
43         ll sum=0;int num=0;
44         for(int j=0;j<n-mid;++j)
45             if((i>>j)&1){
46                 sum+=a[j+mid];++num;
47             }
48         if(!num) continue;
49         ans=min(ans,pair<ll,ll>(Abs(sum),num));
50         it=mp.lower_bound(-sum);  //查找与sum和最小的值在map中位置 
51         //在找到的位置左右分别比较,但不能越界 
52         if(it!=mp.end()) ans=min(ans,pair<ll,ll>(Abs(sum+it->first),it->second+num)); 
53         if(it!=mp.begin()){
54             --it;
55             ans=min(ans,pair<ll,ll>(Abs(sum+it->first),it->second+num));
56         }
57     }
58     printf("%lld %lld\n",ans.first,ans.second);
59 }
60 int main(){
61     while(read()){
62         Solve();
63     }
64     return 0;
65 }
View Code

 

 

 

Subset(POJ-3977)

标签:isp   cond   一个   用法   first   air   1的个数   play   判断   

原文地址:https://www.cnblogs.com/li-jia-hao/p/12772184.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!