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);
}

Submission

posted @ 2026-08-21 20:03  fl0ppy-  阅读(4)  评论(0)    收藏  举报