stl 和并查集应用

抱歉这么久才写出一篇文章,最近进度有点慢。这么慢是有原因的,我在想如何改进能让大家看系列文章的时候更方便一些,现在这个问题有了答案,在以后的推送中,我将尽量把例题和相关知识点在同一天推出,其次在代码分享方面,我也做了改进,对应当加入注释的地方略微加了注释。



先介绍一下今天分享的题目

题目简述:有 n 个人,其中存在很多对朋友关系(不排除有的人没有朋友),这种朋友关系满足对称、传递性质(是否是自反关系对这道题没有影响),即 如果A 和 B 是朋友,就有 B 和 A 是朋友; 如果 A 和 B 是朋友且 B 和 C 是朋友,就有 A 和 C 也是朋友关系。简言之:满足朋友关系的人要么有直接的朋友关系,要么两人有共同的朋友。

由上述特性可以在一个群体间建立起很多个关系网络。

分析一下建立的朋友关系网络具备的特征。对称性说明该关系是无向关系,此外该网络还满足传递性,举个例子:如果其中一个关系网络中有 1 、2、 3、 4  这四个人,

已知

1  < - > 2

2  < - > 3

1  < - > 4

由  1  < - > 2 与 2  < - > 3  可以得出 1 < - > 3

由  1  < - > 2 与 1  < - > 4  可以得出 2 < - > 4

由  2  < - > 3 与 2  < - > 4  可以得出 3 < - > 4

总结:

编号 1 的朋友为:2 、3 、4

编号 2 的朋友为:1 、3 、4

编号 3 的朋友为:1 、2 、4

编号 4 的朋友为:1 、2 、3

由此可以得出结论,这个网络中任意两个人都必须满足朋友关系,且一个人一旦能与该网络某一个人发生关系,此人同样与其他所有人都有关系。如果一个人有朋友,那么这些互为朋友的人必然能够形成一个封闭的关系网络,这就形成了两个极端:要么孤身一人没有朋友,要么就必须和所在关系网络中所有人成为朋友。用图论的知识解释:根据朋友关系的对称性可以得出这个题目中形成的图是无向图,形成的关系网络要么是平凡图(只有一个孤立点),要么是完全图(任意两点之间均连通)。



题目要求:第一行给出总人数 n 和 总关系对数 m,将 n 个人从 1 到 n 进行编号,并给出满足朋友关系的 m 对编号,试判断给出的 m 对关系是否构成了满足题目所述朋友关系的网络体系。是输出 YES ,否则输出 NO 。

输入输出数据要求如下:

m 和 n 均不超过 150 000 。

测试样例如下:



在对题目梗概了解之后,不妨先来观察一下测试样例。

以第一组测试样例为例进行分析:

n = 4 ,m = 3 ,由 3 对朋友关系可以建成如下关系网络体系:

由上图可以看出 左半部分满足完全图,右半部分满足平凡图,该体系满足题目所要求的朋友关系,因此输出答案 YES 。

再来看第二组测试数据:

n = 4 ,m = 4 ,由所给四对关系可以建成如下关系网络体系:

由上图可以看出,4 与 2 、1均未直接连接,因此不满足完全图条件,答案为 NO 。



在对上面两个样例进行分析后发现,判断一个关系网络体系是否满足条件,只需要对每一个连通分量进行判断,如果其中存在一个既不是完全图也不是平凡图的分量,那么这个体系就不满足条件。

人眼去观察的时候,可能一眼就能看出一个连通分量甚至一个关系网络体系是不是满足条件,但是计算机怎么判断呢?计算机判断的时候,必须有一个固定模式形成的标准,用这个标准和形成的实例进行对比,完全匹配的是正确的,不完全匹配则是错误的。这个过程分为以下两步:

第一步:应该建立起输入信息所给的关系网络体系,并将其储存。建立关系网络体系分为两个方面,第一个方面,由于题目要求判断给定的关系是否满足条件,因此要将原来给定的 m 对关系存储,第二个方面,由于朋友关系本身具备的特征,要将对称性和传递性的关系体现出来,也就意味着,在输入的过程中,需要对图进行完善,将对称性和传递性所产生的关系填充。因此,该题目必须建立两个独立的结构,一个单纯存储题目给定的关系,另一个则用来存储经过修正的关系网络体系。

第二步:将原关系和修正关系进行对比。



理论知识介绍完毕,下面是具体的实现方案:

和以往一样,介绍普通思路和修改思路两种方案

方案一

  1. 原关系的存储

    原来的关系给的是 m 对朋友的编号(不重复),由于要满足对称性关系,因此应该存储双向关系,回忆一下,最直观的存储图的结构是--邻接矩阵,说得直白一点就是二维数组,这种结构存储图的好处是,查找任意两个元素之间的关系,时间复杂度是 o(1),但这种结构缺点比较致命,空间利用率不高且对整个图进行遍历时,时间消耗太多。本题m、 n 的范围给的是150 000,开二维数组来实现肯定不现实(我不会告诉你我尝试的时候程序崩溃到连数字都无法读入);另外还有一种方法用来存储图结构--邻接表,这种结构空间利用率是比较高的,但是,链表的实现编写起来比较麻烦,稍有不慎,就会有指针越界的错误出现。不过在对链表操作熟悉的前提下,也不失为一种可行方案。

  2. 修正关系的存储

    修正关系要满足对称性和传递性,根据题目所给的要求,相互之间存在朋友关系的人,放在同一个集合中即可。为什么仅仅存入同一个集合就可以呢?因为本题中任一关系网络均为无向完全图,也就是每个关系网络中的任何一个个体都与同一关系网络内其余所有个体相联系,所有成员不存在特殊需要标记的特性。如果仅仅需要将所有成员分成若干个集合,那么用并查集来实现这一步骤是再合适不过了。

    并查集实现的功能:按照某种特定的关系,将所有元素划分为若干集合,并在每个集合中选出一个代表元素(俗称father)来代表该集合,这种行为类似于在学校这样的大环境中分了若干个班级,每个班级选出一个班长,由班长作为班级的代表和外界进行沟通交流。

  3. 原关系和修正关系的对比

由于修正关系是按照正确的关系建立的关系网络,因此需要判断的是原关系是否有不同于修正关系的地方,即对于修正关系中出现的每一对关系,原关系图中是否存储了该关系。按照这种思路每在修正关系网络中找到一对关系,就需要在存储原关系的邻接矩阵(或邻接表)中查找该关系是否存在。这种思路的时间复杂度远远超过 o(n*n),在150 000 这样规模的数据上是不可取的。

原关系和修正关系的存储结构比较好改进,但是关系对比这个方向怎么改进?既然遍历会超时,能不能换个思路进行改进呢?现在重新捋一下思路,完全图有很多性质,我们能不能利用它的某些性质避开对整个图的遍历呢?对于一个 n 个节点的完全图,其中的每一个节点必然与其余节点相连接,也就意味着与之相关联的关系个数为 n-1,那么只需要确定每个关系网络中所含的节点的个数 n ,再对原图进行遍历,判断每个节点是否与所在关系网络满足上述关系即可。

现在问题转化成了两个简单的问题:

一、求修正关系网络体系中连通分支的个数以及对应的节点数。

二、求解原关系网络体系中与每个节点相关的关系个数并与修正关系中对应的个数进行对比。

准备好所有需要用的数据,只需要一次遍历,时间复杂度为 o(n)就可完成。这就产生了如下方案。

方案二

  1. 原关系的存储

    利用 STL 中的 vector 容器,实现对原关系网络体系的动态存储,开 1e6 的 vector 数组 g[1e6],将与编号 i 相关的所有个体存入 g[i]分量中。这个时候,直接调用 g [i]. size()函数,就可求出与 i 有关的关系个数。(需要注意的一点就是,在此建立的关系是双向的)

  2. 修正关系的存储

    对于修正关系网络体系,可以使用并查集思想对 father 数组进行修正,由于在原关系和修正关系的对比过程中,用到的只有连通分支的代表和节点总数的对应关系,因此,可以利用STL 中的 map 容器,将两者的映射关系存入即可。

  3. 原图和修正图的对比

    逐个比较编号 i 的 分量的关系数 g [i]. size()和 map 中 存储的 i 所在关系网络    的节点数 map[ find( i )]是否满足

    map[ find( i )]-1 = g[i] . size()

这就实现了 建图而避开对图遍历 的目的。



以上是理论介绍,有需要查看代码的朋友打开网页http://paste.ubuntu.com/24324517/。

时间: 2024-10-08 09:04:34

stl 和并查集应用的相关文章

hihocoder 1067 最近公共祖先&#183;二 并查集+stl

题目链接: hihocoder1067 题解思路: 面对10^5个 名字和10^5条询问,肯定要用到特殊的方法: 1.把所有的询问先存下来,然后再遍历一次整棵树得到所有答案 2.遍历的过程中   查询含当前节点的 所有询问,然后找到询问中的另一个节点:查看另一个节点的状态. 如果另一个节点未访问过,接下来处理: 如果另一个节点正在被访问(子节点未访问完),则答案就是这个节点 如果另一个节点已被访问过,则答案是它的正在被访问的根节点 3. 第二点的状态需要并查集处理 未遍历时所有节点标记为未访问

poj 2513 Colored Sticks(欧拉回路 并查集 路径压缩 字典树)(困难)

Colored Sticks Time Limit: 5000MS   Memory Limit: 128000K Total Submissions: 32545   Accepted: 8585 Description You are given a bunch of wooden sticks. Each endpoint of each stick is colored with some color. Is it possible to align the sticks in a st

【bzoj1604】[Usaco2008 Open]Cow Neighborhoods 奶牛的邻居 并查集+Treap/STL-set

题目描述 了解奶牛们的人都知道,奶牛喜欢成群结队.观察约翰的N(1≤N≤100000)只奶牛,你会发现她们已经结成了几个“群”.每只奶牛在吃草的时候有一个独一无二的位置坐标Xi,Yi(l≤Xi,Yi≤[1..10^9]:Xi,Yi∈整数.当满足下列两个条件之一,两只奶牛i和j是属于同一个群的: 1.两只奶牛的曼哈顿距离不超过C(1≤C≤10^9),即lXi - xil+IYi - Yil≤C. 2.两只奶牛有共同的邻居.即,存在一只奶牛k,使i与k,j与k均同属一个群. 给出奶牛们的位置,请计算

【日常学习】【并查集+map】codevs2639 约会计划题解

然而我居然让诸城一中悲剧机房的C++可以编译了··· 直接上题目 题目描写叙述 Description cc是个超级帅哥,口才又好.rp极高(这句话似乎降rp),又非常的幽默,所以非常多mm都跟他关系不错. 然而.最关键的是,cc可以非常好的调解各各妹妹间的关系.mm之间的关系及其复杂.cc必须严格掌握她们之间的朋友关系,好一起约她们出去,cc要是和不是朋友的两个mm出去玩.后果不堪设想-- cc仅仅掌握着一些mm之间的关系.可是cc比較聪明.他知道a和b是朋友,b和c 是朋友,那么a和c也是朋

hdu 2419 逆序操作+并查集

此题关键在于维护点的连通性以及连通块的信息,容易想到并查集,但是并查集却不支持删边操作,于是考虑逆序处理,这样删边就变成了加边操作,每一个连通块的信息可以用stl中的multiset来维护,注意集合合并的时候要启发式合并(这里是按照集合的大小来合并,每次小的集合合并到大的集合里),不然会超时. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <set> 5 u

并查集——家谱

题目描述 现代的人对于本家族血统越来越感兴趣,现在给出充足的父子关系,请你编写程序找到某个人的最早的祖先. 输入格式: 输入文件由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系由二行组成,用#name 的形式描写一组父子关系中的父亲的名字,用+name 的形式描写一组父子关系中的儿子的名字:接下来用?name 的形式表示要求该人的最早的祖先:最后用单独的一个$表示文件结束.规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两个人的名字相同.最多可能有 1000 组父

hdu 1272 小希的迷宫(并查集/附爆栈的原因)

小希的迷宫 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 33194    Accepted Submission(s): 10214 Problem Description 上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的思路不一样,首先她认为所有的通道都应该

1863 畅通工程-并查集最小生成树

题目链接 问题描述: 简单的最小生成树的题,将路径按cost从小到大排序,利用克鲁斯塔尔求最小生成树算法就行. 代码: 1 #include<iostream> 2 #include<queue> 3 #include<algorithm> 4 5 using namespace std; 6 struct Road 7 { 8 int beg; 9 int end; 10 int cost; 11 Road(int beg, int end, int cost) 12

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a