P14361 [CSP-S 2025] 社团招新 题解
P14361 [CSP-S 2025] 社团招新
题意见这里
赛时一眼dp,写了个超级无敌雷霆思维dp,结果样例都过不了(笑
题意挺好懂的,我们考虑贪心
倘若去掉不存在一个部门被分配多于 n/2 个新成员这个限制条件,大家应该都会做,直接选每个人最喜欢的然后累加满意度即可。
要是这也不会建议先看看基础的贪心
好!你既然会搞简单版的,那么这个也不是太难。
我们先感性思考一下,你去报学校,第一志愿滑了是不是肯定要报第二志愿,那这道题也是同理,在一个部门满了之后,需要使原来的总满意值减少得尽可能小(既然事情已经发生了,我们就要让他亏损最小化)。
且假使一个部门人数多于\(\frac{2}{n}\)那么我们知道另外两个部门的人数绝对不会也大于\(\frac{2}{n}\),所以我们直接把一个部门多的人往他的第二志愿放即可,使亏损最小。亏损即为\(最大满意度-次大满意度\),保证最小。
那我们的思路就很清晰了。先无脑让每个人都进自己心仪的部门,假若每个部门人数都满足条件(\(cnt\leq \frac{2}{n}\))那很好了,直接输出即可。
那要是不满足呢?我们直接对于这个多出来的部门做操作,用小根堆存这个部门每个人的亏损,多几个人减去几次最小亏损即可。
代码比较长且屎山,但是应该比较好理解
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
inline int read(){
int x = 0; bool f = 1; char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = 0;
for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return f ? x : -x;
}
const int N = 2e5 + 10;
int t, n;
int brr[N];//记录第i个人去了哪个部门
struct node{
int a, b, c;
}arr[N];
main(){
t = read();
while(t--){
memset(arr, 0, sizeof arr);
memset(brr, 0, sizeof brr);
n = read();
int _m = n / 2;
int numa = 0, numb = 0, numc = 0;//记录每个部门的人数
int noww = 0;//总满意度
for(int i = 1; i <= n; ++i){//先使每个人都到自己最喜欢的部门
arr[i].a = read(), arr[i].b = read(), arr[i].c = read();
if(arr[i].a >= max(arr[i].b, arr[i].c)){
numa++;
brr[i] = 1;
noww += arr[i].a;
continue;
}
else if(arr[i].b >= max(arr[i].a, arr[i].c)){
numb++;
brr[i] = 2;
noww += arr[i].b;
continue;
}
else{
numc++;
brr[i] = 3;
noww += arr[i].c;
continue;
}
}
if(numa <= _m && numc <= _m && numb <= _m){
cout << noww << endl;
continue;//如果都满足 直接输出 无需调整
}
else{
int chao = 0;//记录超出了多少个人
int exceed = 0; //记录是哪个部门不符合条件
if(numa > _m){//第一个部门人多了
chao = numa - _m;
exceed = 1;
}
else if(numb > _m){//第二个部门人多了
chao = numb - _m;
exceed = 2;
}
else if(numc > _m){//同理
chao = numc - _m;
exceed = 3;
}
priority_queue<int, vector<int>, greater<int>> q1;//小根堆存损失
for(int i = 1; i <= n; ++i){
bool need = false;//是否需要调整
if(exceed == 1){
if(!(brr[i] ^ exceed)) need = true;//倘若要调整第一个部门 且当前的人刚好被分到a第一个部门了 就标记他需要被调整 下文同理
}
else if(exceed == 2){
if(!(brr[i] ^ exceed)) need = true;
}
else{
if(!(brr[i] ^ exceed)) need = true;
}
if(!need) continue;
int loss = 0;
if(exceed == 1){
loss = arr[i].a - max(arr[i].b, arr[i].c);//最大减去次大得出损失
}
else if(exceed == 2){
loss = arr[i].b - max(arr[i].a, arr[i].c);
}
else{
loss = arr[i].c - max(arr[i].a, arr[i].b);
}
q1.push(loss);
}
int aaa = q1.size();
while(q1.size() > aaa - chao){
noww -= q1.top();
q1.pop();
}
cout << noww << endl;
}
}
exit(0);
}
浙公网安备 33010602011771号