【codevs3372】选学霸,并查集+可达性DP

选学霸
时间限制: 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
写在前面:感谢Shallwe的帮助
——————————————————————————————————————————————
思路:用并查集初始化,然后当作可达性背包来做(由于数据比较弱,没有O(15000*30000)的卡人数据)就可以了
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
int father[30010],num[30010],a[30010];//a数组存放物品标号,num存放每个物品的价值(对于选i来说必须要选的人数)
bool f[60010];
int n,m,k,ans;
int find(int x)
{
    if (x!=father[x]) father[x]=find(father[x]);
    return father[x];
}
void pd(int x,int y)
{
    if (abs(m-x)<abs(m-y)) ans=x;
}
main()
{
    int x,y,p,q;
    int t;
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++) father[i]=i;
    for (int i=1;i<=k;i++)
    {
        scanf("%d%d",&x,&y);
        p=find(x);q=find(y);
        if (p!=q) father[p]=q;
    }
    for (int i=1;i<=n;i++) 
    {
        t=find(i);
        num[t]++;
        if (num[t]==1) a[++a[0]]=t;
    }
    f[0]=1;
    for (int i=1;i<=a[0];i++)
    for (int j=2*m;j>=num[a[i]];j--)
    f[j]=max(f[j-num[a[i]]],f[j]);
    for (int i=m;i>=0;i--) if (f[i]) {ans=i;break;}
    for (int i=m;i<=2*m;i++)
    if (f[i]) pd(i,ans),printf("%d",ans),exit(0);
    printf("%d",ans);
}

你可能感兴趣的:(【codevs3372】选学霸,并查集+可达性DP)