AtCoder Grand Contest 029 B-Power of two 题解

题目链接 https://atcoder.jp/contests/agc029/tasks/agc029_b

这题题意为n个正数要求两两搭配形成"二进制数"(2^n这样的数,例如2 4 8 16...),数字可以不用完(一般想用也用不完),且每个数字只能用一次.  

例: 3 11 14 5 13最多组成16(3+13) 16(5+11)这两个“二进制数”。

面对这题,很多人会陷入选择困难症,因为可选择的组合的结果——目标数——“二进制数”很多样,上一个例子就有3+5=8这种因选择错误而导致得不偿失的坏结果。

一旦算法要考虑的过多,程序就不好写,太多要考虑了。

 

所以找对方法,这题AC不是梦。

 

我听说过两种方法:一种是把这题转化成图论做的:每个数字作为一个点,和其他点若能形成“二进制数”就产生一条连线,最后就想办法统筹最多的路线(思路应该是这样子,图论这方面我完全不会,就讲这么多,当这个方法”抛砖引玉”吧);

另一种是想办法避免做选择,实现方法是从大的数字开始为这个数字找伙伴,假设当前最大数是a,而a的组成目标仅仅能是大于a的最小二进制数(设为target),因为我们是从最大的数字到最小的数字开始找伙伴的,所以a和当前第二大的数字b的和 <= 2*a < 2*target,  也就是说,a的组成目标不可能是比target更大的“二进制数”(2*target),也不可能是比target更小的"二进制数"(因为a>=target/2)。 那好了,我们知道手上的一个材料且知道唯一的目标,那么查找另一个材料(target-a)(下称作b)存在否就解决了眼前的问题(这就是贪心),以此类推。

 

实现方法是用map存储输入数据,键是数字本身,附加元素是该数字的个数,遇到如上查找能匹配的对立即把所有能转换的都转换掉

#include<iostream>
#include<algorithm>
#include<cmath>
#include<map>
#include<functional>
using namespace std;
#define maxn long long(5*1e8)

int main(){
    long long n=0;
    scanf("%d",&n);    
    map<long long,long long,greater<long long>> num;
    for(long long i=1;i<=n;++i){
        long long temp=0;
        scanf("%d",&temp);
        num[temp]++;
    }
    //目标“二进制数”
    long long j=pow(2.0,31),ans=0;
    for(auto iter=num.begin();iter!=num.end();++iter){
        while(iter->first<j/2) j/=2;//减小目标,找到合适的目标(大于iter->first的最小"二进制数")
        if(num[iter->first]>0&&num[j-iter->first]>0){
                //特殊情况,iter->first自己就是二进制数
                if(j==iter->first*2){
                    ans+=num[iter->first]/2;
                    num[iter->first]=num[iter->first]%2;
                }
                else{    //贪心取完
                    ans+=min(num[iter->first],num[j-iter->first]);
                    num[j-iter->first]-=min(num[iter->first],num[j-iter->first]);
                }
            }
    }
    cout<<ans<<endl;
}
View Code

 

(可以证明,现在立刻取完b或a不会导致坏结果(即得不偿失,凑得对变少),取完a显然是因为a已经没有“利用价值”了;取完b的话,读者可能担心以后b与其他数配对的情况,下给出证明(若有a个1,b个3,和c个7,这种情况配对的最多”二进制数”为min(a,b+c),当a<c时,并不影响最终结果就是min(a,b+c)=a))。

 

posted @ 2020-09-20 21:45  Laozhu1234  阅读(237)  评论(0)    收藏  举报