您的位置:首页 > 其它

codevs3370 选学霸(背包dp,并查集)

2017-01-24 16:57 330 查看

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

/*利用并查集把有关连的人和在一起当一个物品,再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;
}


心若向阳,无言悲伤
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: