题目已经说明,要想不抗议,要么选,要么不选,所以把实力相当的人合并成一人,并且修改体积。
这里涉及合并操作, siz[t1]+=siz[t2] 也是权值并查集的精华
然后进行01背包,dp[n]代表n人最多能选出的人数,求出之后,去和m最接近且最小的即可
题目描述
老师想从 nn 名学生中选 mm 人当学霸,但有 kk 对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 mm 尽可能接近。
输入格式
第一行,三个正整数 n,m,kn,m,k。
第 22 至第 kk 行,每行 22 个数,表示一对实力相当的人的编号(编号为 1,2,...,n1,2,...,n)。
输出格式
一行,表示既不让同学们抗议,又与原来的 mm 尽可能接近的选出学霸的数目。
如果有两种方案与 mm 的差的绝对值相等,选较小的一种。
输入输出样例
输入 #1复制
4 3 2 1 2 3 4
输出 #1复制
2
说明/提示
对于 100\%100% 的数据,满足 1 \le n,m \le 2 \times 10^41≤n,m≤2×104
# include<iostream>
# include<math.h>
# include<algorithm>
using namespace std;
typedef long long int ll;
int f[20000+10];
int getf(int x)
{
if(x==f[x])
return x;
else
{
f[x]=getf(f[x]);
return f[x];
}
}
int siz[20000+10];
int len;
int v[20000+10];
int dp[20000+10];
int main ()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
f[i]=i;
siz[i]=1;
}
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int t1=getf(x);
int t2=getf(y);
if(t1!=t2)
{
f[t2]=t1;
siz[t1]+=siz[t2];
}
}
if(!m)
{
cout<<0;
return 0;
}
if(!k)
{
cout<<m;
return 0;
}
for(int i=1;i<=n;i++)
{
if(f[i]==i)
{
v[len]=siz[i];
len++;
}
}
for(int i=0;i<len;i++)
{
for(int j=n;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
}
}
int min1=9999999;
int cnt=0;
for(int i=1;i<=n;i++)
{
if(min1>abs(dp[i]-m))
{
min1=abs(dp[i]-m);
cnt=dp[i];
}
}
cout<<cnt;
return 0;
}