专题·并查集【including 并查集基础,村村通

初见安~这里是一个差点被遗忘了的并查集专题:)

并查集

顾名思义——并查集,就是合并,搜查集合。其本质意义为:

有n个集合的数,我们为了区别这几个集合,每个集合选择一个数作为代表,看某两个数是否在同一集合中,则只需看它们所在集合的代表数是否相同。

如果还没理解的话:

则我们可以设1为集合1的代表,4为集合2的代表。

在数组fa(father)中就可以存x点的所在集合代表:

fa[x]=1(1\leqslant x\leqslant3 );

看两个点是否在同一集合中,只要看两点的fa是否一样即可。

然而——

多数情况下我们是不可能一开始就确定每个点直属的集合的,往往是一种间接的关系。比如在上图中,实际情况可以是:

fa[3]=2;

fa[2]=1;

fa[1]=1;

fa[8]=6;

fa[6]=5;

fa[7]=5;

fa[5]=4;

fa[4]=4;

但我们仍然知道,1、2、3三个点是在同一集合里的。所以这时候我们就需要一个并查集专用函数之一:get操作来找点x真正的所在集合的代表点。

由上方情况我们可以得知:一个点如果它本身就是该集合的代表点,会有fa[ x ] = x。同理,就有了以下操作:

int get(int x)
{
	if(fa[x]==x) return x;//已经到了这个集合的代表节点,返回即可。
	return get(fa[x]);//否则继续递归找其父节点。
}

大致就是这个样子了:

当然,每个点在连边之前各自的根节点就是它自己,初始化为fa[ x ] = x;

所以有时我们会发现:递归的层数有可能会很大,甚至有时候如果在无向图中知道a、b相连,fa存fa[ a ] = b或者fa[ b ] = a都有可能判定的时候会出现有两个点的根连不到一块儿去的情况。即设a本就在集合1中,fa[ a ] = b后本应将b拉入集合1,却变成了a、b点在外单独成立一个集合的状态。所以我们在存fa的时候,存的是点的根节点相连。即

fa[ get(a) ] = get( b );

这就是并查集的正常操作了:)用到并查集的算法:最小生成树·Kruskal


下面我们来看一个例题:【这里是传送门:洛谷P1536

题目描述

某市调查城镇交通状况,得到现有城镇道路统计表。表中列出了每条道路直接连通的城镇。市政府“村村通工程”的目标是使全市任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要相互之间可达即可)。请你计算出最少还需要建设多少条道路?

输入格式:

每个输入文件包含若干组测试测试数据,每组测试数据的第一行给出两个用空格隔开的正整数,分别是城镇数目N(N<1000)和道路数目M;随后的M行对应M条道路,每行给出一对用空格隔开的正整数,分别是该条道路直接相连的两个城镇的编号。简单起见,城镇从1到N编号。

注意:两个城市间可以有多条道路相通。例如:

3 3 1 2 1 2 2 1 这组数据也是合法的。当N为0时,输入结束。

输出格式:

对于每组数据,对应一行一个整数。表示最少还需要建设的道路数目。

输入样例#1:

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

输出样例#1:

1
0
2
998

题解:

这是一个很基础的纯并查集操作题:已有的边全部连上,可以将图划分为n个互不连通集合,看有多少个集合就需要再连多少个-1条边来把它们连起来。

下面是代码及详解——

#include<bits/stdc++.h>
using namespace std;

int f[2000];//fa数组
int get(int x)
{
    if(f[x]==x) return x;
    return get(f[x]);
}

int main()
{
    register int m,n,a,b,ans=0;
    while(scanf("%d",&n))
    {
        if(n==0) return 0;//读入完毕
        cin>>m;
        ans=0;
        for(register int i=1;i<=n;i++)
            f[i]=i;//初始化
            
        for(register int i=1;i<=m;i++)
        {
            cin>>a>>b;
            f[get(a)]=get(b);//存图连边
        }
        
        for(register int i=1;i<=n;i++)
        {
            if(f[i]==i) ans++;//有多少个根节点就是有多少个集合
        }
        cout<<ans-1<<endl;//n个集合需要连n-1条边,树形
    }
    return 0;
}

迎评:)

——End——

猜你喜欢

转载自blog.csdn.net/qq_43326267/article/details/87551097
今日推荐