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

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

 

posted @ 2017-01-24 16:57  安月冷  阅读(146)  评论(0编辑  收藏  举报