P2170 选学霸
解法:我们可以把k对利用并查集链接起来,之后我们可以把每一个连通块当作一个背包的物品,因为题意求的是abs(选择的学霸 - m),我们的背包体积开成2 * m即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 100;
int fa[maxn],d[maxn],a[maxn],dp[maxn];
int find(int x){
if(x == fa[x])return x;
else return fa[x] = find(fa[x]);
}
void merge(int a, int b){
int x = find(a),y = find(b);
fa[x] = y;
}
int main(){
int n,m,k;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++)fa[i] = i,d[i] = 0;
for(int i = 1; i <= k; i++){
int u,v;
cin >> u >> v;
merge(u, v);
}
for(int i = 1; i <= n; i++){
int x = find(i);
d[x] += 1;
}
int pos = 0;
for(int i = 1; i <= n; i++){
if(d[i] != 0){
a[++pos] = d[i];
}
}
for(int i = 1; i <= pos; i++){
for(int j = 2*m; j >= a[i]; j--){
dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
}
}
int ans = 1e9,mx = 1e9;
for(int i = 1; i <= 2*m; i++){
if(mx > abs(dp[i] - m))mx = abs(dp[i] - m),ans = dp[i];
}
if(ans == 1e9)cout << "0" << '\n';
else cout << ans << '\n';
}