【CodeVS3372】选学霸
Description
老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近。
Input
第一行,三个正整数N,M,K。
第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)。
Output
一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种。)
Sample Input
4 3 2
1 2
3 4
Sample Output
2
HINT
100%的数据N,P<=30000
题解
先并查集,把实力相同的人合并,然后01背包(v是2倍,因为是绝对值之差最小),找出最接近的装满的取差比较大小。
注意 并查集完成以后要
for (int i=1;i<=n;i++) f[i]=root(i);
举个例子
f 1 2 3 4 5 6
(1 3) 1 2 1 4 5 6
(5 6) 1 2 1 4 5 5
(1 5) 1 2 1 4 1 5 (f[6]还是5)
(小醇发现的!小醇实在是太机智了!)
#include<iostream> #include<cstdio> using namespace std; int f[30010],d[30010],v[30010],a[60010]; int n,m,k,cnt,x,y; int root (int x){ if (f[x]!=x) f[x]=root(f[x]); return f[x]; } int main(){ scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) f[i]=i; for (int i=1;i<=k;i++){ scanf("%d%d",&x,&y);if (root(x)!=root(y)) f[f[x]]=f[y]; } for (int i=1;i<=n;i++) f[i]=root(i); for (int i=1;i<=n;i++) d[f[i]]++; for (int i=1;i<=n;i++){ if(d[i]){ v[++cnt]=d[i]; } } n=cnt; for (int i=1;i<=n;i++) for (int j=2*m;j>=v[i];j--) a[j]=max(a[j],a[j-v[i]]+v[i]); int i; for (i=m+1;i<=2*m;i++) if (a[i]==i) break; if (min(m-a[m],i-m)==m-a[m]) cout<<a[m];else cout<<i; }