题目链接: Wonderful Coloring - 2
大致题意
有一个长度为n的序列, 你有m种颜色, 编号1~m. 你要选择给每个位置涂上某种颜色或不涂色.
要求: ①最终每种颜色被涂色的位置数量相同 ②不同位置的相同数字不能涂同种颜色 ③最大化每种颜色的涂色数量
输出最终的涂色策略.
解题思路
贪心 本题思维基于B1的做法.
我们首先考虑对于序列的所有数字从小到大排序, 那么对于相同的数字而言, 如果数字个数大于m, 则我们应该舍弃多余的数字. 然后对于相同的这部分逐一涂色.
由于我们需要保证每种颜色的使用次数尽可能平均, 因此涂色的m种颜色, 我们不妨想想成它们连成环. 对于每个要涂色的位置我们进行1~m成环涂色.
如n = 7, m = 3, 序列排序后: 1 2 2 2 2 3 3
则我们涂色序列为 1 2 3 1 0 2 3 即可. 注意对于2这个数字, 我们进行了
2 3 1
顺序涂色.
这样做有一个问题: 我们最后可能不能恰好涂完一个循环, 我们需要考虑到撤销涂色操作, 即: 把最后一次不完整的环涂色撤销掉.
具体做法: 我们存储序列按照 { 值, 下标 } 存储, 然后将整个序列从小到大排序. 用flag
记录当前该使用的颜色. 同时开一个桶来存储每个值出现的次数, 如果出现次数已经大于m了, 则直接不涂色.
最后判断, 如果最后的几次涂色不是一个完整的循环, 则撤销.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 2E5 + 10;
pair<int, int> a[N];
int res[N];
int main()
{
int t; cin >> t;
while (t--) {
int n, m; scanf("%d %d", &n, &m);
rep(i, n) {
int x; scanf("%d", &x);
a[i] = { x, i };
}
sort(a + 1, a + 1 + n);
unordered_map<int, int> mp;
int flag = 0;
rep(i, n) {
auto& [val, pos] = a[i];
mp[val]++;
if (mp[val] > m) res[pos] = 0;
else res[pos] = flag++ % m + 1;
}
if (flag % m) {
for (int i = n; i >= 1; --i) {
auto& [val, pos] = a[i];
if (res[pos]) res[pos] = 0, flag--;
if (flag % m == 0) break;
}
}
rep(i, n) printf("%d%c", res[i], " \n"[i == n]);
}
return 0;
}