洛谷 P1525 关押罪犯 NOIp2010提高组 (贪心+并查集)

题目链接:https://www.luogu.org/problemnew/show/P1525

题目分析

  通过分析,我们可以知道,这道题的抽象意义就是把一个带边权的无向图,分成两个点集,使得两个集合中的每两个点之间的边权最大的最小。问这个边权是多少。

  我们不妨可以想一下,如果$a$和$b$是敌人,但$a$和$b$的有些敌人不是敌人,对于处理$a$来说,肯定是把他和那些$b$的敌人放在一起比较好。    再来看下题目,尽可能地让最大的最小,所以我们可以贪心一下下~~~   把所有的“敌人对”按照边权从大到小排个序。 然后按顺序判$a$ ,$b$在不在一个点集里头(这里便用到了并查集,直接查父亲是不是一个就好),如果在,就可以输出边权了。如果不在,把$a$和$b$的敌人放在一起,$b$和$a$的敌人放在一起(参见上上行)。

  肯定有人要问为什么。呃。。 因为边权是排过序的,第一个匹配不上的肯定是最优解。(此处需脑补一下。。)

  呃。。然后就可以开始码代码了。

  

#include <stdio.h>
#include <algorithm>
using namespace std;
const int M=100003;
const int N=20003;
struct node
{
    int x, y, z;
}f[M];
int a[N];
int b[N];
bool cmp(node x,node y)
{
    return x.z > y.z;
}
int find(int x)
{
    if(a[x] == x)
        return x;
    a[x] = find(a[x]);
    return a[x];
}
void update(int x,int y)
{
    int fx = find(x);
    int fy = find(y);
    a[fx] = fy;
}
bool check(int x,int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx == fy)
        return true;
    return false;
}
int main()
{
    int n, m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n; i ++)
        a[i]=i;
    for(int i = 1;i <= m; i ++)
        scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].z);
    sort(f+1,f+1+m,cmp);
    for(int i = 1;i <= m+1; i ++)
    {
        if(check(f[i].x,f[i].y))
        {
            printf("%d",f[i].z);
            break;
        }
        else
        {
            if(!b[f[i].x])
                b[f[i].x]=f[i].y;
            else
                update(b[f[i].x],f[i].y);
            if(!b[f[i].y])
                b[f[i].y]=f[i].x;
            else
                update(b[f[i].y],f[i].x);
        }
    }
    return 0;
}

 

posted @ 2018-09-13 20:43  sky20030724  阅读(153)  评论(2)    收藏  举报