题目:https://www.luogu.org/problemnew/show/P2170
题目描述
老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近
输入输出格式
输入格式:
第一行,三个正整数N,M,K。
第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)
输出格式:
一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种:)
输入输出样例
说明
100%的数据N,P<=20000
解析
唉,我并查集都忘得差不多了orz。
如果一些人的实力相同,那么他们中要么全选,要么不选。
这像不像01背包呢?
用并查集处理水平相同的人,如果相同归在一块,看成一个整体。
这个整体的 价值 和 所占用的体积 都为这个整体的人数。
然后就是一个背包dp啦,可以用f[i]表示体积为i能放多少人,如果f[i]==i则代表这种方案合法。
当然也可以采用可行性dp(本文即是),f[i]表示i人能否被取到。
没有得满分的dalao们注意啦:
①你有可能一个人都不选
②不要码错并查集(估计只有我这么。。。。。。)
③计算每一个整体的时候,判断某个属于哪个集体时用find(x)而不是fa[x](见代码)
④没了,祝您ak愉快
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 using namespace std; 8 #define maxn 200100 9 int n,m,k,ra,rb; 10 int fa[maxn]; 11 bool f[2*maxn]; 12 int groupr[maxn]; 13 int group[maxn],tot; 14 int finda(int x){ 15 if (fa[x]==x) return fa[x]; 16 else return fa[x]=finda(fa[x]); 17 } 18 void unionn(int x,int y){ 19 int fx=finda(x),fy=finda(y); 20 if (fx==fy) return; 21 fa[fx]=fy; 22 } 23 int main(){ 24 scanf("%d%d%d",&n,&m,&k); 25 for (int i=1;i<=n;++i) fa[i]=i; 26 for (int i=1;i<=k;++i){ 27 scanf("%d%d",&ra,&rb); 28 //if (ra>rb) swap(ra,rb); 29 unionn(ra,rb); 30 } 31 for (int i=1;i<=n;++i){ 32 groupr[finda(i)]++; //不要写成fa[i] 33 } 34 for (int i=1;i<=n;++i){ 35 if (groupr[i]){ 36 group[++tot]=groupr[i]; 37 } 38 } 39 f[0]=true; 40 for (int i=1;i<=tot;++i){ 41 for (int j=2*m;j>=group[i];--j){ 42 if (f[j-group[i]]){ 43 f[j]=true; 44 } 45 } 46 } 47 for (int i=0;i<=m;++i){ 48 if (f[m-i]){ 49 printf("%d",m-i); 50 return 0; 51 } 52 if (f[m+i]){ 53 printf("%d",m+i); 54 return 0; 55 } 56 } 57 return 0; 58 }