您的位置:首页 > 其它

105.(并查集结合绝对值最小的01背包)选学霸

2016-03-21 06:30 253 查看


3372 选学霸[/b]

时间限制: 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


分类标签 Tags 点此展开

动态规划 背包型DP 并查集 树结构
基本思路:

输入时,采用并查集,把实力相同的人合并到一个集合,在一个循环,统计每个集合的人数,就像是物品

的体积,背包最多的体积不超过m,取与m之差的绝对值最小的那种方案(跑绝对值之差最小的01背包就行)。
代码:

#include<
cstdio >
#include<
iostream >
using namespace
std;
int
father[30001],f[30001*2];
int
wp1[30001]={0},wp[30001]={0};
int n,m,k;
int find(int
x)
{
if(father[x]!=x)
father[x]=find(father[x]);
return
father[x];
}
void unionn(int
a,int b)
{

father[b]=a;
}
void
input()
{

scanf("%d%d%d",&n,&m,&k);
for(int
i=1;i<=n;++i)

father[i]=i;
int a,b;
for(int
i=1;i<=k;++i)
{

scanf("%d%d",&a,&b);
int
r1=find(a);
int
r2=find(b);
if(r1!=r2)

unionn(r1,r2);
}
}
int main()
{
input();
for(int
i=1;i<=n;++i)
{
int
r1=find(i);
wp1[r1]++;
}
int t=0;
for(int
i=1;i<=n;++i)
{
if(wp1[i])
{
++t;

wp[t]=wp1[i];

}
}

for(int
i=1;i<=t;++i)

for(int j=2*m;j>=0;j--)

{

if(j-wp[i]>=0)

{

f[j]=max(f[j],f[j-wp[i]]+wp[i]);

}

}
int
people1=m-f[m];
int p;
int
people2;
for(int
j=m+1;j<=2*m;++j)
if(f[j]==j)
{
p=j;

people2=f[j];
break;
}
people2-=m;

if(people1>people2)

printf("%d\n",f[p]);

else

printf("%d\n",f[m]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: