cf1342 D. Multiple Testcases(思维,贪心)
题意:
把 \(n\) 个数分成尽量少的 \(t\) 组。每组中大于等于 \(i\) 的数不能超过 \(c_i\) 个。输出 \(t\) 和一种分组方案。
1 <= ai <= k
思路:
注意 \(c_i\) 之间是包含关系,相当于每个值的容量的后缀和。 \(c_1\ge c_2\ge\cdots\)
因为题目给的是大于等于 \(i\) 的个数限制,所以考虑先放较大的数。
设 \(cnt[x]\) 表示原数组中 \(x\) 出现的次数。先放原数组中最大的数 \(maxx\) ,那么 \(t\) 需满足 \(t*c[maxx]\ge cnt[maxx]\)。然后放第二大的数 \(maxx-1\),要求 \(t*c[maxx-1]\ge cnt[maxx]+cnt[maxx-1]\)。然后放第三大的数 \(maxx-2\),要求 \(t*c[maxx-2]\ge cnt[maxx]+cnt[maxx-1]+cnt[maxx-2]\)。
以上所有不等式须同时满足。如果把 \(cnt[]\) 变成后缀和,就是 \(t*c_x\ge cnt_x\) 对定义域内所有 \(x\) 成立。所以 \(t=max\{\lceil cnt_x/c_x \rceil\}\)
接下来从大到小放数,第 \(1\) 到 \(t\) 组每次放一个,然后再在第 \(1\) 到 \(t\) 组每组放一个,放完为止,这样即可保证大的数先被满足。实测从小到大放数也可以。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, k, a[N], c[N], cnt[N];
vector<int> ans[N];
signed main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), cnt[a[i]]++;
for(int i = 1; i <= k; i++) scanf("%d", &c[i]);
for(int i = k-1; i >= 0; i--) cnt[i] += cnt[i+1]; //后缀和
int t = 1;
for(int x = 1; x <= k; x++) t = max(t, (int)ceil(1.0*cnt[x]/c[x]));
printf("%d\n", t);
sort(a + 1, a + 1 + n, greater<int>());
for(int i = 1; i <= n; i++) ans[i%t].push_back(a[i]);
for(int i = 0; i < t; i++) //输出方案
{
printf("%d ", ans[i].size());
for(int j : ans[i]) printf("%d ", j);
puts("");
}
return 0;
}

浙公网安备 33010602011771号