老师想从 n 名学生中选 m 人当学霸,但有 k 对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。
所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 m 尽可能接近。
思路
注意:是与m的差的绝对值最小化,所以选的人数可>m,也可≤m
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+5;
int n,m,k, fa[N], sz[N], f[N], s[N];
int find(int u) {
return fa[u]==u ? u : fa[u]=find(fa[u]);
}
void merge(int u, int v) {
int fu=find(u), fv=find(v);
if (fu!=fv) {
fa[fu]=fv;
sz[fv]+=sz[fu];
}
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>k;
for (int i=1; i<=n; i++) fa[i]=i, sz[i]=1;
for (int i=0; i<k; i++) {
int u,v; cin>>u>>v;
merge(u,v);
}
int tot=0, cap=min(n,2*m);
for (int i=1; i<=n; i++) if (fa[i]==i)
s[tot++]=sz[i]; //每个连通块的人数
//对于每个连通块都可以选或不选
for (int i=0; i<tot; i++)
for (int j=cap; j>=s[i]; j--) {
f[j]=max(f[j], f[j-s[i]]+s[i]);
}
ll ans=-1, mi=INT_MAX;
for (int j=0; j<=cap; j++) {
int d=abs(f[j]-m);
if (d<mi || (d==mi && f[j]<ans)) {
mi=d, ans=f[j];
}
}
cout<<ans;
return 0;
}