codevs3370 选学霸(背包dp,并查集)
3372 选学霸
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 大师 Master
题目描述 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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*利用并查集把有关连的人和在一起当一个物品,再01背包 此处背包无需考虑价值,只需用bool,true能取,false不能取 注意都不取的情况 */ #include <iostream> #include <cstring> #include <cmath> using namespace std; const int maxn=20005; int n,m,k,tot; int p[maxn]; int man[maxn]; int f[maxn+maxn]; int find(int x) { return (x==p[x]?x:p[x]=find(p[x])); } void solve() { memset(f,0,sizeof(f)); for(int i=1; i<=tot; i++) for(int j=2*m; j>=man[i]; j--)//2*m因为要求与m的绝对值 f[j]=max(f[j],f[j-man[i]]+man[i]);//01背包 for(int i=0; i<=m; i++) { if(f[m-i]==m-i) { cout<<m-i<<endl; return; } if(f[m+i]==m+i) { cout<<m+i<<endl; return; } } return; } int main() { cin>>n>>m>>k; int u,v; for(int i=1; i<=n; i++) p[i]=i; for(int i=1; i<=k; i++) { cin>>u>>v; int x=find(u); int y=find(v); p[x]=y; } memset(man,0,sizeof(man)); for(int i=1; i<=n; i++) man[find(i)]++; tot=0; for(int i=1; i<=n; i++) if(man[i]!=0) man[++tot]=man[i]; solve(); return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。