并查集

并查集

 ̄綄美尐妖づ 提交于 2020-01-23 22:30:40
并查集 并查集算法实际上是找不相交的集合的算法。 例如:有n个人,m种关系,如果a认识b,b认识c,那么a能通过b认识c,最后能有几个朋友圈。 刚开始时,每个人都是独立的,假如有总共有五个人,建立一个一维数组s[6](需要进行初始化),s[1]~s[5]分别表示这五个人,数组的下标分别代表他们朋友圈中的那个“boss”,通过给出的关系,来合并朋友圈。 假设每个人最开始的“boss”是自己,s[1]~s[5]的值等于它的boss是谁,它们最开始的值等于他们的下标。 样例输入: 9 8 1 2 3 4 5 2 4 6 2 6 9 7 8 7 1 6 样例输出: 2 通过给出的关系合并朋友圈,第一组中,1和2是朋友,根据“靠左”法则,2的boss是1,那么s[2]的值变为1。相同的,第二组中,3和四是朋友,所以s[4]的值变为3。 而第三组中,5和2是朋友,这时关系发生冲突,将5就变成了1和2的boss,s[5]=s[1]=s[2]=5,这样就保留了之前的关系。第四组中s[4]=s[6]=4,第五组中,s[2]=s[5]=s[1]=s[6]=5,第六组中,s[9]=s[7]=9, 第七组中,s[8]=s[7]=s[9]=8,第八组中,s[1]=s[5]=s[2]=s[6]=s[4]=5。 所以这个过程就是找boss,找到树的根结。 最开始时,他们的boss都是自己。 void init(

并查集

◇◆丶佛笑我妖孽 提交于 2020-01-23 22:27:57
例题 最开始每个人都是单独的个体 for ( int i = 1 ; i <= n ; i ++ ) a [ i ] = i ; 现在给出他们相互之间的关系,我们相对应进行处理。但我们都知道,人类的关系错综复杂,一个体系中不会有单纯的一对一关系,他们之间可能会有重叠,所以我们需要进行“路径压缩”。 int root ( int x ) { if ( x == a [ x ] ) return x ; else { a [ x ] = root ( a [ x ] ) ; return a [ x ] ; } } 注意数组前后的变化,判断最终结果 操作运行一遍,无误,完整代码如下 # include <stdio.h> int a [ 1000100 ] ; int root ( int x ) { if ( x == a [ x ] ) return x ; else { a [ x ] = root ( a [ x ] ) ; return a [ x ] ; } } int main ( ) { int n , m ; while ( ~ scanf ( "%d%d" , & n , & m ) ) { int ans = 0 ; for ( int i = 1 ; i <= n ; i ++ ) a [ i ] = i ; for ( int i = 0 ; i < m ;

并查集C语言

北战南征 提交于 2020-01-23 22:22:26
并查集 什么是并查集 并查集是用来处理“有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中”的问题的。通常我们使用的数据结构空间复杂度还可以但时间复杂度远远超出我们所需求的。而并查集这种树状数据结构正好可以处理这种一些不相交集合的合并及查询问题,大大减少了我们的时间复杂度。 简单的来说就是一个把自己的根列出来,然后再查找自己的根的在哪的问题。 怎么实现并查集 首先并查集有三各要素需要明确:根,树枝,查找路径。悬空说很难落实下面我们引入例题: 这是一道很明显的并查集的题目,我们先将每个人认识的头目(根)列到root数组中去,然后我们通过判断他们的根是否一致来实现计算其数量也就是我们说的查找路径 下面是代码实现: int search ( int men ) { int now ; now = men ; while ( root [ men ] != men ) { men = root [ men ] ; } //查找根 if ( now != men ) { root [ now ] = root [ men ] ; } //压缩路径 return men ; } 我们通过压缩路径来减少下次判断的时间,然后我们只要再对根的一致进行判断就行了。下面是总代码的实现: #

并查集

可紊 提交于 2020-01-23 22:16:27
并查集就是最开始又N个集合,这N个集合当中有一部分集合存在关联,就可以把有关联的集合 合并 在一起,这个关联也可以间接建立,所以要 查找 是否间接存在关联,最后得出剩余的集合数。 题目描述 有n个人,编号1-n。 现在有一个舞会,在舞会上,大家会相互介绍自己的朋友。 即: 如果a认识b,b认识c。那么在舞会上,a就会通过b认识到c。 现在,给出m个关系 每个关系描述: a b 表示 编号为a和编号为b的人是朋友关系。 输入格式 输入n和m 接下来m行,每行为a b 输出格式 最后问,会有多少个朋友圈。 样例输入 5 3 1 2 2 3 4 5 样例输出 2 步骤: 初始化 先建立一个数组,用数组下标表示集合。 void fun ( int n ) { for ( int i = 1 ; i <= n ; i ++ ) p [ i ] = i ; } 查找 因为每个集合最开始的值就是下标,代表还没有合并。当输入1 2时,说明1和2集合要合并为同一个集合。方法就是把p[1]的值由1变成2,也就是现在1和2就同属于一个集合里面。随后输入2 3。同理把p[2]的值变成3。集合又减少一个。 这一步通过find函数来实现。 b1 = find ( p1 ) ; b2 = find ( p2 ) ; if ( b1 != b2 ) { p [ b1 ] = b2 ; } int find(

并查集

让人想犯罪 __ 提交于 2020-01-23 16:52:37
一, 并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题,常常在使用中以森林来表示。 二,主要操作 1.初始化 把每个点所在集合初始化为其自身。 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。 2.查找 查找元素所在的集合,即根节点。 3.合并 将两个元素所在的集合合并为一个集合。 通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。 3.例题:朋友圈 有n个人,编号1-n。 现在有一个舞会,在舞会上,大家会相互介绍自己的朋友。 即: 如果a认识b,b认识c。那么在舞会上,a就会通过b认识到c。 现在,给出m个关系 每个关系描述: a b 表示 编号为a和编号为b的人是朋友关系。 最后问,会有多少个朋友圈。 代码实现 # include <stdio.h> int unionsearch ( int root ) ; int db [ 5000000 ] //每个朋友圈的代表,如db[2]=3指编号为2的人朋友圈的代表是编号为3的人 int main ( ) { int n , m , pyq , i , start , end , root1 , root2 , sum = 0 ; while ( scanf ( "%d" , & n ) != EOF ) { scanf ( "%d"

并查集

血红的双手。 提交于 2020-01-23 11:01:41
心得:看那个博客的时候,感觉很生动很形象,一下子就看懂了,在写那个题目的时候,按照给的方法来做,有问题,仔细检查之后,发现那个路径压缩函数好像没有用到,于是我就把第一次调用过的函数再调用一次,这样就可以知道有几个掌门了,emmm,这个题目我觉得只要细心,应该还是没问题的。 原理:就是把一个人用a[i]中的i表示,假设每个人都有且只知道他的上级,如a[i]=t;上级或许还有上级,直到a[i]=i位置,就找到了上级是谁,然后两个人碰面,确定是不是友人,就看掌门是否一样,然后就一层一层往上问,最终确定是不是同一个掌门,路径压缩函数就是让每一个人都知道他们的掌门是谁,方便确定是不是盟友。 主要代码 int unionsearch(int root) //查找根结点 { int son, tmp; son = root; while(root != pre[root]) //我的上级不是掌门 root = pre[root]; while(son != root) //我就找他的上级,直到掌门出现 { tmp = pre[son]; pre[son] = root; son = tmp; } return root; //掌门驾到~~ }(找掌门的, 红色段是路径压缩函数,不过一开始好像没有用到这个) void join(int root1, int root2) (两个人碰面,看是不是盟友

可持久化并查集

时光总嘲笑我的痴心妄想 提交于 2020-01-22 21:03:09
合并不采用路径压缩,保证每次合并只修改一个节点的父亲,使当前版本与上一版本共用的节点尽可能的多 为防止并查集退化成链,采取按秩合并 \(code\) : void build(int L,int R,int &cur) { cur=++tree_cnt; if(L==R) { fa[cur]=L; return; } int mid=(L+R)>>1; build(L,mid,ls[cur]); build(mid+1,R,rs[cur]); } void merge(int L,int R,int pos,int fath,int pre,int &cur) { cur=++tree_cnt; if(L==R) { fa[cur]=fath; de[cur]=de[pre]; return; } ls[cur]=ls[pre],rs[cur]=rs[pre]; int mid=(L+R)>>1; if(pos<=mid) merge(L,mid,pos,fath,ls[pre],ls[cur]); if(pos>mid) merge(mid+1,R,pos,fath,rs[pre],rs[cur]); } int query(int L,int R,int pos,int cur) { if(L==R) return cur; int mid=(L+R)>>1; if(pos<

[JSOI2008]最大数(并查集)

邮差的信 提交于 2020-01-22 19:59:30
并查集的神奇用法: [JSOI2008]最大数 Description 现在请求你维护一个数列,要求提供以下两种操作: 1、 查询操作。 语法: Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。 限制: L L不超过当前数列的长度。 (L > 0) ( L > 0 ) 2、 插入操作。 语法: A n 功能:将 n n加上 t t,其中 t t是最近一次查询操作的答案(如果还未执行过查询操作,则 t=0 t = 0),并将所得结果对一个固定的常数 D D取模,将所得答案插入到数列的末尾。 限制: n n是整数(可能为负数)并且在长整范围内。 注意:初始时数列是空的,没有一个数。 输入格式 第一行两个整数, M M和 D D,其中 M M表示操作的个数 (M \le 200,000) ( M ≤ 2 0 0 , 0 0 0 ), D D如上文中所述,满足 (0<D<2,000,000,000) ( 0 < D < 2 , 0 0 0 , 0 0 0 , 0 0 0 ) 接下来的 M M行,每行一个字符串,描述一个具体的操作。语法如上文所述。 输出格式 对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。 Solution 首先我们来观察这道题的性质: 所有的查询都是从最后开始的 这意味着甚么?

并查集-G - 食物链

别等时光非礼了梦想. 提交于 2020-01-21 22:52:45
G - 食物链 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类。 第二种说法是"2 X Y",表示X吃Y。 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。 你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 Input 第一行是两个整数N和K,以一个空格分隔。 以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 若D=1,则表示X和Y是同类。 若D=2,则表示X吃Y。 Output 只有一个整数,表示假话的数目。 Sample Input 100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5 Sample Output 3 1 #include<algorithm>

清北学堂—2020.1提高储备营—Day 2 morning

我与影子孤独终老i 提交于 2020-01-20 21:53:13
qbxt Day 2 morning ——2020.1.18 济南 主讲:李佳实 目录一览 1.并查集 2.堆 总知识点:基础数据结构 一、并查集 1.描述:并查集是一类十分常用的数据类型,它有着十分广泛的应用。在信息竞赛中,它主要执行的操作一般有三种。 (1) 合并a,b两个元素所在的集合 Merge(a,b) (2)查找某个元素属于哪个集合 find(k) (3)查询两个元素是否属于同一集合 Query(a,b) 2.函数模板 (1)find int find(int x){ if(fa[x]==x) return x; //找到即返回 int t=find(fa[x]); //继续递归find return t; } (2)Merge void merge(int x,int y){ x=find(x); y=find(y); if(x==y) return; //根相同,无需合并,即返回 fa[x]=y; //根不同,即合并 } 3.对于find和merge的优化 (1) 对于merge:启发式合并(使用次数少) 描述:在合并集合S1、S2的时候,我们让较小的树成为较大的树的子树。这里可以是深度、节点个数等启发函数来比较树的大小(一般使用深度)。 代码实现会使用到并查集,而且并不常用,暂且略。 (2) 对于find:路径压缩(常用,效率高,代码简单) 描述