在 3 个部门中最大化总满意度,同时每个部门人数不能超过 n/2。
P14361 [CSP-S 2025] 社团招新
反悔贪心:先让每个人去最满意部门,再把超额部门中损失最小的人换出去。
一、先回答问题
一开始就试图同时平衡三个部门,容易写成复杂匹配;本题只会有一个部门超额。
二、核心观察
建模先得到不受限制的最优分配,再只修正唯一可能超额的部门。
优化超额部门内按换出损失排序,取最小的 need 个。
正确性所有人起点都是个人最优;若必须换出若干人,选择损失最小者不会比选择损失更大者差。
三、互动演示
--
--
--
1:准备开始。
四、自己选答案
为什么最多只有一个部门会超额?
先自己判断,再点选项。
如果某人从超额部门换走,应换到哪里?
先自己判断,再点选项。
五、手推结果
| 步骤 | 要看什么 | 结论 |
|---|---|---|
| 1 | 每个人的最大满意度 | 先求没有限制时的最大总和 |
| 2 | 三个部门人数 | 若都不超过 n/2,答案已经最优 |
| 3 | 超额部门每人的换出损失 | 损失越小,越适合被换出去 |
| 4 | 需要换出的人数 | cnt[d] - n/2 |
六、C++14 参考代码
可提交代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<array<int, 3>> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i][0] >> a[i][1] >> a[i][2];
}
long long ans = 0;
vector<int> belong(n);
int cnt[3] = {0, 0, 0};
for (int i = 0; i < n; i++) {
int best = 0;
for (int j = 1; j < 3; j++) {
if (a[i][j] > a[i][best]) best = j;
}
belong[i] = best;
cnt[best]++;
ans += a[i][best];
}
int limit = n / 2;
for (int d = 0; d < 3; d++) {
if (cnt[d] <= limit) continue;
vector<int> loss;
for (int i = 0; i < n; i++) {
if (belong[i] != d) continue;
int otherBest = 0;
for (int j = 0; j < 3; j++) {
if (j != d) otherBest = max(otherBest, a[i][j]);
}
loss.push_back(a[i][d] - otherBest);
}
sort(loss.begin(), loss.end());
int need = cnt[d] - limit;
for (int i = 0; i < need; i++) ans -= loss[i];
break;
}
cout << ans << '\n';
}
return 0;
}
`belong[i]` 记录初始最优部门。
`loss` 中只放超额部门的人。
答案用 `long long` 保存。