【解题报告】luogu P1525 关押罪犯(并查集/种类并查集)

原题地址ios

题意

给n个节点,m条无向的边,要求把这n个节点分配到两个集合当中,保证两个集合中存在同一集合的两点存在的边的权值的最大值最小,若是能够把节点分配到两个集合中保证每一个集合中任意两点间均不存在边,则输出0。web

思路

开一个两倍节点数大小的数组做为并查集,第一倍保存同一集合中的节点的祖先,第二倍保存不一样集合中的点的祖先,将所给边从大到小排序,再从大到小遍历每一条边,若是边的两个节点第一倍的祖先不一样,则将两个节点分配到不一样的集合当中,即合并第一个节点的第一倍集合与第二个节点的第二倍集合,同时合并第二个节点的第一倍集合和第一个节点的第二倍集合。若是遍历到的边的两个节点的第一倍集合的祖先相同,则说明这两个节点必在同一集合里,这也是最优的状况了,此时输出权值,程序结束。不然完成遍历尚未找到,则输出0。数组

上代码svg

#include <iostream>
#include <algorithm>
using namespace std;
int n, m;
int fa[40005];
int findfather(int x)
{
    return x==fa[x]?x:fa[x] = findfather(fa[x]);
}
struct Edge
{
    int x, y;
    int c;
    bool operator < (const Edge a)
    {
        return c>a.c;
    }
}e[100005];
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1; i<=2*n; i++)
    {
        fa[i] = i;
    }
    for(int i=1; i<=m; i++)
    {
        cin>>e[i].x>>e[i].y>>e[i].c;
    }
    sort(e+1, e+m+1);
    for(int i=1; i<=m; i++)
    {
        int t1 = findfather(e[i].x);
        int t2 = findfather(e[i].y);
        int t3 = findfather(e[i].x+n);
        int t4 = findfather(e[i].y+n);
        if(t1 != t2)
        {
            fa[findfather(e[i].x+n)] = findfather(e[i].y);
            fa[findfather(e[i].y+n)] = findfather(e[i].x);
        }
        else
        {
            cout<<e[i].c<<endl;
            return 0;
        }
    }
    cout<<"0"<<endl;
    return 0;
}