并查集题目整理

若如初见. 提交于 2020-04-04 09:15:02

并查集

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

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

知识回顾

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

<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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!