题目链接: Phoenix and Towers
大致题意
给你n个数, 要求你把这些数字分成m组, 使得任意两组之间的差值不超过x.
解题思路
贪心 这有点像之前cf的一个B题, 大概是说放积木, 问最少能放几层.
我们遍历n个数字, 每次把当前这个数字放到总和最小的组里即可.
因为我们最后要求任意两组之前的差值尽可能的小, 相当于让m组数尽可能的平均. 我们贪心去做即可.
到这里你发现, 可能我这样的做法并不会使得这m组数尽可能的平均, 因为我们也没有排序.
给出一个例子: 对于3个数, 分成2组的情况. 1 2 7
一开始的两组的情况为{ 0, 0 }, 那么第一个数字, 变为{ 0, 1 }, 再放完第二个数字, 变为{ 1, 2 }. 再放第三个数字, 变为 { 2, 8 }. 但是很明显, 我们可以构造出 { 3, 7 }的方法, 这样会更优.
但是本题的特点是, 如果要求的差值是x, 数字给的范围为[1, x], 也就是说, 不会有任意两个数字, 使得他们之间的差值 ≥ x, 因此如果我们每次直接以上述贪心的方式去放, 也不会使得他们之间的差值超过x.
这里可以采用反证法, 如果放完val后, 此时存在两组a和b, 满足其差值大于x, 那么说明val一定可以放在较小的那组数中, 使得差值≤ x, 与我们最开始的贪心做法相矛盾.
由上述推到我们也可以证明, 本题是一定存在解的, 并不存在 “NO” 的情况.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1E5 + 10;
int res[N];
int main()
{
int t; cin >> t;
while (t--) {
priority_queue<PII, vector<PII>, greater<>> q; //构造小顶堆
int n, m, k; scanf("%d %d %d", &n, &m, &k);
rep(i, m) q.push({ 0, i }); //第二维是组数的下标
rep(i, n) {
int x; scanf("%d", &x);
auto op = q.top(); q.pop();
op.first += x; res[i] = op.second;
q.push(op);
}
puts("YES");
rep(i, n) printf("%d%c", res[i], " \n"[i == n]);
}
return 0;
}