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

  • 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

分类标签 Tags 点此展开 

基本思路:
输入时,采用并查集,把实力相同的人合并到一个集合,在一个循环,统计每个集合的人数,就像是物品
的体积,背包最多的体积不超过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;
}
posted @ 2016-03-21 06:30  csgc0131123  阅读(255)  评论(0编辑  收藏  举报