题目描述 Description
老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近。
输入描述 Input Description
第一行,三个正整数N,M,K。
第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)。
输出描述 Output Description
一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种。)
样例输入 Sample Input
4 3 2
1 2
3 4
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
100%的数据N,P<=30000
实力相当的人要尽量选上,因此我们可以利用并查集把实力相当的人合并,每个集中的老大都有自己的盟友(实力相当的人)(第i个人盟友个数用w[i]表示,初始化w[i]=1),当合并两个集时(f[a]=b),要注意把a的盟友同时合并给b,即w[b]+=w[a],a的盟友自然成了b的盟友,a现在只是一个小喽罗,所以w[a]=0;现在开始枚举w[i]不为0的人,则可以把一个个集看作一个个团队(也可以是单人团队),w[i]表示团队人的个数,++k,最终k表示团队的个数。问题转化为了01背包问题。但是注意此问题要求的是差的绝对值最小的值,所以可以从0开始向两边枚举(枚举dp[m-i]是否等于m-i,dp[m+i]是否等于m+i),第一个枚举到的值输出即可。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cmath> 5 #include<vector> 6 #include<algorithm> 7 #include<cstring> 8 #include<vector> 9 #include<map> 10 #include<stack> 11 #define maxn 30000+50 12 #define inf 0x7fffffff 13 #define xiao 1e-9 14 using namespace std; 15 int f[maxn],w[maxn],a[maxn],dp[2*maxn]; 16 int find(int x) 17 { 18 if(f[x]==x) return x; 19 else return find(f[x]); 20 } 21 int main() 22 { 23 int n,m,k,b,x; 24 cin>>n>>m>>k; 25 for(int i=1;i<=n;++i){f[i]=i;w[i]=1;} 26 for(int i=1;i<=k;++i) 27 { 28 scanf("%d%d",&x,&b); 29 int fa=find(x),fb=find(b); 30 if(fa!=fb) 31 { 32 f[x]=b; 33 w[b]+=w[x]; 34 w[x]=0; 35 } 36 } 37 k=0; 38 for(int i=1;i<=n;++i) 39 { 40 if(w[i]!=0) a[++k]=w[i]; 41 } 42 for(int i=1;i<=k;++i) 43 { 44 for(int j=2*m+1;j>=a[i];--j) 45 { 46 dp[j]=max(dp[j],dp[j-a[i]]+a[i]); 47 } 48 } 49 for(int i=0;i<=m;++i) 50 { 51 if(dp[m-i]==m-i){cout<<m-i;break;} 52 if(dp[m+i]==m+i) {cout<<m+i;break;} 53 } 54 return 0; 55 } 56