选拔赛d

题意:有一个数列a,不超过500个数,均小于1e9,里面的数两两相加出来一个新数列b,然后把a,b混合成新数列c并把c打乱,告诉你c中数的个数以及每一个数,求a。
蒟蒻如我场上写了个爆搜…..emmmmm…..
正解应该基于如下想法考虑:我们可以将c从小到大排序,然后对于c中的每一个数,他要么是a中的数要么是能被a中两个数表示出来的;因此我们可以考虑每一个数是否能被现有的确定的a中的数表示出来,如果表示得出来说明这个数就是b中的数,否则把这个数加进a。(准确来说是在考虑每一个数出现的次数,如果在a中的数相加已经可以满足当前扫到的数出现的次数,那么这个数肯定是被表示出来的。否则不是)
用unordered_map会比map快。

#include<cstdio>
#include<iostream>
#include<map>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cmath>
#include<ctime>
#include <unordered_map>
using namespace std;
unordered_map<int,int>f;//键值1是数值,键值2是这个数值出现的次数
vector<int>ans;
int main()
{
    freopen("1.in","r",stdin);
    int m,n,i,j,k,num[150000];
    k=clock();
    while(cin>>m){
        n=sqrt(m*2);
        ans.clear();f.clear();
        for(i=1;i<=m;i++)scanf("%d",&num[i]),f[num[i]]++;
        sort(num+1,num+1+m);
        for(i=1;i<=m;i++){
            if(f[num[i]]){//如果这个数值还存在的话,说明前面的数并不能表示出来
                // (或者能表示出来但是次数不足,因此他依然得是a中的数)
                f[num[i]]--;
                for(j=0;j<ans.size();j++) {
                    f[num[i] + ans[j]]--;//新加入了数,那么能表示出来很多新数
                }
                ans.push_back(num[i]);
            }
        }
        cout<<ans.size()<<endl;
        for(i=0;i<ans.size();i++)
            printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
        cout<<clock()-k<<endl;
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/humveea6/article/details/79926438