Codeforces 1342D Multiple Testcases(贪心)

题目链接

题目大意

  给(n)个不大于(k)的数,让你尽可能的把它们分成较小的组,每组大于等于(i)的数不超过(c[i])个。

分析

  从最简单的情况开始考虑,对于(n)个数中最大的数(i),如果数组中一共有(m_i)(i),那么这个数组中大于等于(i)的数的数量就是(m_i)个,所以说将这(m_i)个数分组至少需要(lceil {m_idiv c[i]} ceil)组。如果是(i-1)呢,那么大于等于(i-1)的数的数量就是(m_i+m_{i-1}),需要分的组数至少需要(lceil {(m_i+m_{i-1})div c[i-1]} ceil)组。等等,是不是忘了什么?我们前面分的组数和后面不一定是相等的,所以要想满足每个条件,肯定要取最大的组数。我们知道了组数了具体该怎么分呢?经过前面的计算我们已经得到了最小可行的组数,所以我们只要按顺序把各个数均摊到我们分的组里就行了。

具体实现

  我们可以先用一个桶来计数,算出每个数字出现的次数,然后倒着求后缀和,就能得到大于当前数字的数量,后面求最大分组就很容易了(记得向上取整)。后面的分组可以用模运算。

代码

const int maxn = 2e5+10;
vector<int> ans[maxn];
int cnt[maxn], c[maxn];
int main(void) {
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 0, num; i<n; ++i) {
        scanf("%d", &num);
        ++cnt[num];
    }
    for (int i = 1; i<=k; ++i) scanf("%d", &c[i]);
    int sum = 0, maxx = -1;
    for (int i = k; i>=1; --i) {
        sum += cnt[i+1];
        //cout << sum << endl;
        maxx = max(maxx, (sum+c[i]-1)/c[i]);
    }
    int tot = 0;
    for (int i = k; i>=1; --i)
        while(cnt[i]) {
            ans[tot%maxx].push_back(i);
            --cnt[i];
            ++tot;
        }
    printf("%d
", maxx);
    for (int i = 0; i<maxx; ++i)
        if (!ans[i].empty()) {
            printf("%d", (int)ans[i].size());
            for (auto num : ans[i]) printf(" %d", num);
            putchar(endl);
        }
    return 0;
}
原文地址:https://www.cnblogs.com/shuitiangong/p/12820121.html