zoukankan      html  css  js  c++  java
  • 并查集题目整理

    并查集

    之前写最小生成树的时候对这一部分的知识也并没有十分详细的整理

    近天做了一些用到并查集的题目,来整理一下

    知识回顾

    首先,先来回顾一下有关并查集的内容

    <1> 定义

    并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

    集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

    <2>初始化

    把每个点所在集合初始化为其自身。

    通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。

    <3>查找

    查找元素所在的集合,即根节点。

    <4>合并

    将两个元素所在的集合合并为一个集合。

    通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的"查找"操作实现。

    主要处理的是有关这一部分的两个例题

    Luogu P1550 [USACO08OCT]Watering Hole G

     解题思路:

    这个题给我的第一印象是最小生成树的Kruscal

    但是仔细一读题发现其实有许多小的细节与最精简的Kruscal是有差别的

    例如题目中有一个挖井的操作

    那就讲问题拆开来看

    part1:如何解决连牧场的操作

    这里不难想到去直接最小生成树维护

    很显然牧场之间的连线满足生成树的性质

    part2:如何解决挖井的问题

    考虑在不影响part1的前提下实现

    既然每个牧场挖井都有一定的价格

    不难发现这类似于每个牧场与井的连线的价格

    考虑将每个牧场与井连线

    将井作为单独的第n+1个点去考虑

    直接与part1的最小生成树一起操作

    代码

    #include<bits/stdc++.h>
    using namespace std;
    struct edge
    {
        int from,to,dis;
    }e[500001];
    bool cmp(edge a,edge b)
    {
        return a.dis<b.dis;
    }
    int father[200001],w[200001];
    int getfather(int x)
    {
        if(father[x]==x)
            return x;
        father[x]=getfather(father[x]);
        return father[x];
    }
    int n,cnt,sum;
    long long ans;
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>w[i];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                sum++;
                e[sum].from=i;
                e[sum].to=j;
                cin>>e[sum].dis;
            }
        for(int i=1;i<=n;i++)
        {
            sum++;
            e[sum].from=i;
            e[sum].to=n+1;
            e[sum].dis=w[i];
        }
        for(int i=1;i<=n+1;i++)
            father[i]=i;
        sort(e+1,e+sum+1,cmp);
        for(int i=1;i<=sum&&cnt<=n;i++)
        {
            int fu=getfather(e[i].from),fv=getfather(e[i].to);
            if(fu==fv)
                continue;
            father[fu]=fv;
            cnt++;
            ans+=e[i].dis;
        }
        cout<<ans;
        return 0;
    }

    Luogu P2078 朋友

     解题思路:

    主要还是考虑女士为负数

    轻松想到了map,它可以定义任意类型下标

    当然也可以将他通过奇妙的操作变为正数,比如加2n来与n进行区分,大致思路没有任何区别

    我们发现每个公司都只有同性,那肯定是俩公司一起搭配。

    通过这俩人配怎么办?

    并查集!每个公司中分别找和这个公司中的媒人祖先相同的点,统计一下个数,最后俩数取小

    通俗一点就是将两个公司认识(直接或间接【并查集】)小明或小红的人数统计

    最后取min就可以了,毕竟是一夫一妻制

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,z,x,y,m,b,q;
    map<int,int>fa;
    void init()
    {
        for(int i=-1*m;i<=n;i++)
            fa[i]=i;
    }
    int get(int x)
    {
          if(fa[x]==x)
              return x;
          int r=get(fa[x]);
          fa[x]=r;
          return r;
    }
    void merge(int x,int y)
    {
        int r1=get(x);
        int r2=get(y);
        if(r1==r2)
            return;
        fa[r1]=r2;
    }
    int main()
    {
        cin>>n>>m>>b>>q;
        init();
        merge(1,-1);
        for(int i=1;i<=b+q;i++)
        {
            cin>>x>>y;
            merge(x,y);
        }
        int ans=0,an=0;
        for(int i=1;i<=n;i++)
            if(get(1)==get(fa[i]))
                ans++;
        for(int i=-1*m;i<0;i++)
            if(get(-1)==get(fa[i]))
                an++;
        cout<<min(ans,an)<<endl;
        return 0;
    }
  • 相关阅读:
    JS获取四位年份和2位年份
    notebook 快捷键
    发表文章不需要版面费的期刊
    命题演算、集合论和布尔代数之间的关系是什么?
    炒作套路
    如何理解佛经中“黄叶止啼”的故事
    期货之为什么要注册仓单!逼空是什么鬼!
    反证法与归谬法的区别
    感恩的含义!告诉你什么是感?什么是恩?人生必读!
    悖论的本质
  • 原文地址:https://www.cnblogs.com/gongcheng456/p/12628386.html
Copyright © 2011-2022 走看看