标签: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 }
标签:isp cond 一个 用法 first air 1的个数 play 判断
原文地址:https://www.cnblogs.com/li-jia-hao/p/12772184.html