P14361 [CSP-S 2025] 社团招新 题解
题目传送门
思路
题目要求求最大值,考虑贪心
因为满意度的定义为 \(\sum_{i=1}^{n} a_{i,d_j}\),则可以将每一\(max(a_{i,d_j})\)放入社团\(j\)中,此时满意度一定是\(max\)的。而题目规定任意社团总人数不能超过\(\frac{n}{2}\)人,所以要考虑有超出限制的情况。注意到若某一社团的人数超过\(\frac{n}{2}\),则另两个社团的总人数一定不超过\(\frac{n}{2}\),且不会有两个社团的人数同时超过\(\frac{n}{2}\),就可以将超出限制的部分分到另外两个社团。此时,另外两个社团的人数一定是小于\(\frac{n}{2}\)的,给出如下证明:
设\(x,y,z\)分别为三个社团的人数,其中\(\frac{n}{2}<x<n,1\leq y,z\leq \frac{n}{2}\)。将\(x\)分给\(y,z\)后,\(x=\frac{n}{2},y+z=\frac{n}{2}\),又因为\(y,z\geq 1\)所以一定满足\(y,z<\frac{n}{2}\)
因此我们无需考虑将超出限制的部分分给其他两个社团后超出限制的情况
为了使在限制下满意度为\(max\),我们需要使损失的满意度最小,即取\(min(max(a_{i,d_j}) - \operatorname{secondmax}(a_{i,d_j}))\),我们可以在输入时就预处理出每个学生满意度的最小损失值。至此我们发现,除了在计算最小损失值时我们并没有使用任意\(d_j\),所以我们无需存储,直接在循环内计算最小损失值和无限制的最大满意度即可。再把最小损失值放入每个社团的数组,判断是否有数组的\(size>\frac{n}{2}\)。如没有就直接输出最大满意度;如有,则对超出限制的数组进行排序,遍历超出限制部分的最小损失值,用无限制的最大满意度减去即可,最后输出。
\(AC~Code\)(可读性不佳):
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--){
int sd[MAXN];
int n,maxx,ans=0;
cin>>n;
maxx=n/2;
vector<int> a;
vector<int> b;
vector<int> c;
for(int i=1;i<=n;i++){
pair<int,int> tmp[3];
for(int i=0;i<=2;i++){
cin>>tmp[i].first;
tmp[i].second=i;
}
sort(tmp,tmp+3);
ans+=tmp[2].first;
sd[i]=tmp[2].first-tmp[1].first;
if(tmp[2].second==0) a.push_back(sd[i]);
else if(tmp[2].second==1) b.push_back(sd[i]);
else c.push_back(sd[i]);
}
if(a.size()>maxx){
sort(a.begin(),a.end());
for(int i=0;i<a.size()-maxx;i++){
ans-=a[i];
}
}else if(b.size()>maxx){
sort(b.begin(),b.end());
for(int i=0;i<b.size()-maxx;i++){
ans-=b[i];
}
}else if(c.size()>maxx){
sort(c.begin(),c.end());
for(int i=0;i<c.size()-maxx;i++){
ans-=c[i];
}
}
cout<<ans<<'\n';
}
}

浙公网安备 33010602011771号