返回顶部

牛客编程巅峰赛S1第11场 - 黄金&钻石 C.牛牛找子集 (二分)

  • 题意:有一\(n\)个数,从中找数构成相同的子集,要求子集元素个数为\(k\),求构成子集个数最多的情况,输出子集(字典序最小).

  • 题解:我们可以对子集的个数二分答案,首先用桶记录每个元素的个数,然后二分找最大值,check函数中直接枚举\([1,100000]\)的个数,然后用\(mid\)去除,得到的\(t\)就是它可以贡献给子集的元素个数,枚举完之后,比较一下总贡献和\(k\)即可.然后\(l\)就是我们得到的最大答案,再次枚举\([1,100000]\),求每个数的贡献\(t\),然后塞\(t\)个自己进去给\(ans\),当\(cnt=k\)时,直接return \(ans\)即可.

  • 代码:

    class Solution {
    public:
        /**
         * 返回找到能够满足游戏胜利条件的子集,如果有多个子集满足条件,返回字典序最小的即可。
         * @param n int整型 代表数字的数量
         * @param k int整型 代表子集的大小
         * @param s int整型vector 代表数字数组
         * @return int整型vector
         */
        int mp[1000010];
        int l,r;
        vector<int> res;
        
        bool check(int x,int k){
            int sum=0;
            for(int i=1;i<=100000;++i){
                int t=mp[i]/x;
                if(t>=1) sum+=t;
            }
            if(sum>=k) return true;
            else return false;
        }
        
        vector<int> solve(int n, int k, vector<int>& s) {
            // write code here
            for(int i=0;i<n;++i){
                mp[s[i]]++;
            }
            l=1,r=1;
            for(int i=1;i<=100000;++i){
                r=max(r,mp[i]);
            }
            while(l<r){
                int mid=(l+r+1)>>1;
                if(check(mid,k)){
                    l=mid;
                }
                else r=mid-1;
            }
            int cnt=0;
            for(int i=1;i<=100000;++i){
                int t=mp[i]/l;
                if(t>=1){
                    for(int j=0;j<t;++j){
                        res.push_back(i);
                        cnt++;
                        if(cnt==k) return res;
                    }
                }
            }
            return res;
        }
    };
    
posted @ 2020-08-14 01:13  _Kolibri  阅读(177)  评论(0)    收藏  举报