并查集是一种用于处理一些不相交集合(Disjoint Sets)的合并及查询等问题的树形数据结构。
下面我们举个例子来引入对并查集的理解。
大家都看过古惑仔吧,在香港呢分布着很多的帮派,而每个帮派都有一个老大,比如什么洪兴老大蒋天生,铜锣湾陈浩南。各个帮派人数众多,小弟们通过大哥们相连,而大哥的大哥的最终大哥就是共同的老大,所以拐了很多个弯最终还是同个帮派的人,由共同的老大连接着,这些众多的帮派的原理也就是我们所说的并查集。而某两个古惑仔有了矛盾,而不知道对面是哪个帮派的不敢下手,毕竟别打了自己人,所以就需要确认一下, 报出自己直接上级的大哥肯定不稳妥,毕竟一个帮派的大哥有那么多,而帮派老大只有一个,只要看看老大是不是一样就知道是不是属于同一个帮派啦,这里一层层地上溯寻找最终老大的过程就是查找。若某天,一个帮派的老大很佩服另外一个帮派的老大, 自愿做了另外一个老大的小弟(虽然几率很小),这样两个帮派就成了一家人,无论小弟有多少,之后都共同信奉一个老大,这就是合并的过程。
顾名思义,并查集包含两种基本操作:
(1)查找(Find)。查找元素所在的集合即根节点。
(2)合并(Combine)。先判断两个元素是否在同一集合,若否,则将 两个集合合并。
下面给出代码帮助理解:
int pre[1050];//pre数组用于记录根节点void Init(int n) //初始化结点,将根节点的pre[i]置为它本身,n为节点数
{for(int i=0;i<=n;i++)pre[i]=i;
}int Find(int x)
{int r=x;while(r!=pre[r])//判断是否为根节点r=pre[r];return r;//返回根节点
}void Combine(int x,int y)
{int fx=Find(x),fy=Find(y);if(fx!=fy)//若根节点不同{pre[fy]=fx;//将其中一个置为另一个的根节点,实现合并}
}
再用刚才的例子,如果小弟们都很喜欢做大哥,小弟又收了小弟,这样一层一层收下去,很容易造成关系复杂,最底层的小弟需要上溯很多层才能找到最后的老大,因此,我们优化一下帮派的结构,将除了老大的所有人都置为老大的小弟,这样就 实现了优化。
HDU-1213-How Many Tables
题目里的桌子数其实就是我们介绍的帮派数,判断多少个根节点就得出了所需的桌子数。下面给出代码及注释
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;int pre[1050];//pre数组用于记录根节点
int t[1050]={0};//用于判断是否为根节点
void Init(int n) //初始化结点
{for(int i=0;i<=n;i++)pre[i]=i;
}int Find(int x)
{int r=x;while(r!=pre[r])//判断是否为根节点r=pre[r];int i=x,j;while(pre[i]!=r)//优化过程,最终pre[i]都指向根节点{j=pre[i];pre[i]=r;i=j;}return r;//返回根节点
}void Combine(int x,int y)
{int fx=Find(x),fy=Find(y);if(fx!=fy)//若根节点不同{pre[fy]=fx;//将其中一个置为另一个的根节点,实现合并}
}int main()
{int n,N,M,i,a,b,ans;scanf("%d",&n);while(n--){scanf("%d%d",&N,&M);Init(N);while(M--) //吸收并整理数据{scanf("%d%d",&a,&b);Combine(a,b);}memset(t,0,sizeof(t));for(i=1;i<=N;i++) //标记根结点{t[Find(i)]=1;//同时实现优化}for(ans=0,i=1;i<=N;i++)//ans记录所需桌子数if(t[i])ans++;printf("%dn",ans);}return 0;
}
同类例题:
HDU-1232-畅通工程
本文发布于:2024-02-02 21:26:24,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170688038546540.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |