Codeforces 1342D Multiple Testcases(贪心)

题目链接

题目大意

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

分析

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

具体实现

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

代码

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\n", 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;
}
posted @ 2020-05-02 22:57  shuitiangong  阅读(162)  评论(0编辑  收藏  举报