zoukankan      html  css  js  c++  java
  • 种类并查集(关押犯人)

    emmm又是一个很高大上的概念

    很久前就接触过,可惜直到现在还是摸棱两可。

    核心概念就是:朋友的朋友是我的朋友

           敌人的敌人是我的敌人

    常见的做法是将原并查集扩大一倍规模,并划分为两个种类。

    在同个种类的并查集中合并,和原始的并查集没什么区别,仍然表达他们是朋友这个含义。

    考虑在不同种类的并查集中合并的意义,其实就表达 他们是敌人 这个含义了。

    按照并查集美妙的 传递性,我们就能具体知道某两个元素到底是 敌人 还是 朋友 了。

    如果a和b是敌人,合并n+b和a,n+a和b

    如果c和a是敌人,合并n+c和a,n+a和c

    那么b和c就并在一起了

    因为a成为了他们的纽带

     那么,我们来看一下这道题目。关押犯人https://www.luogu.com.cn/problem/P1525

    题面:n个犯人,m对关系,表示犯人A和犯人B的冲突值。

    现在要把犯人分成两类,要求在同一个监狱中犯人的最大冲突值最小。

    可以注意到,要减小冲突值,首先我们不能把冲突最大的分到一起。

     比如,我们知道1和2肯定在不同的监狱,因为他们的冲突值太大了。

    然后我们再考虑,在这个基础上,4和3也最好也在不同的监狱

    同理,1和3也是这样,2和3,以此类推

    但是我们不能无限这样做下去,因为我们不能保证都在不同的监狱。

    我们这么想:1和2是敌人

          4和3也是敌人

          1和3是敌人

          2和3是敌人

    是不是发现,敌人的敌人就是我的朋友.

    然后我们枚举到2和3的时候,发现他们是朋友

    不可避免地,2和3冲突了。

    #include <bits/stdc++.h>
    using namespace std;
    int pre[40009],n,m,l,r;
    struct p{
        int l,r,v;
        bool operator < (const p&tmp)    const{
            return v>tmp.v;
        }
    }a[100009];
    int find(int x){
        if(x!=pre[x])    pre[x]=find(pre[x]);
        return pre[x];
    }
    void join(int q,int w){
        pre[find(q)]=find(w);
        return;
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=m;i++)    cin>>a[i].l>>a[i].r>>a[i].v;
        sort(a+1,a+1+m);
        for(int i=1;i<=n+n;i++)    pre[i]=i;
        for(int i=1;i<=m;i++)
        {
            int l=a[i].l,r=a[i].r;
            if(find(l)==find(r))
            {
                cout<<a[i].v;
                return 0;
            }
            join(l+n,r);
            join(r+n,l);
        }
        cout<<0;
    }
    View Code

  • 相关阅读:
    Golang 数组
    Golang 定时任务库使用
    Golang 选项设计模式
    使用阿里云构建海外docker镜像
    大数相加算法实现
    递归思想和迭代思想
    阶乘函数(factorial)——结果在整型范围内的阶乘计算
    最大公约数和最小公倍数(Greatest Common Divisor and Least Common Multiple)
    日结(2018年12月23日)
    日结(2018年12月22日)
  • 原文地址:https://www.cnblogs.com/iss-ue/p/12463054.html
Copyright © 2011-2022 走看看