【CodeVS3372】选学霸

Description

老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近。

Input

第一行,三个正整数N,M,K。

第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)。

Output

一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种。)

Sample Input

4 3 2

1 2

3 4

Sample Output

2

HINT

100%的数据N,P<=30000

题解

先并查集,把实力相同的人合并,然后01背包(v是2倍,因为是绝对值之差最小),找出最接近的装满的取差比较大小。

注意 并查集完成以后要

    for (int i=1;i<=n;i++) f[i]=root(i);

举个例子

f 1 2 3 4 5 6

(1 3) 1 2 1 4 5 6

(5 6) 1 2 1 4 5 5 

(1 5) 1 2 1 4 1 5 (f[6]还是5)

(小醇发现的!小醇实在是太机智了!)

#include<iostream>
#include<cstdio>
using namespace std;
int f[30010],d[30010],v[30010],a[60010];
int n,m,k,cnt,x,y;

int root (int x){
    if (f[x]!=x) f[x]=root(f[x]); return f[x];
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=k;i++){
        scanf("%d%d",&x,&y);if (root(x)!=root(y)) f[f[x]]=f[y];
    }
    for (int i=1;i<=n;i++) f[i]=root(i);
    for (int i=1;i<=n;i++) d[f[i]]++;
    for (int i=1;i<=n;i++){
        if(d[i]){
            v[++cnt]=d[i];
        }
    }
    n=cnt;
    for (int i=1;i<=n;i++)
        for (int j=2*m;j>=v[i];j--)
        a[j]=max(a[j],a[j-v[i]]+v[i]);
    int i;
    for (i=m+1;i<=2*m;i++) if (a[i]==i) break;
    if (min(m-a[m],i-m)==m-a[m]) cout<<a[m];else cout<<i;
}
原文地址:https://www.cnblogs.com/liumengyue/p/5271895.html