并查集

PAT (Advanced Level) Practice 并查集 相关题

  • 《算法笔记》 重点摘要
  • 1034 Head of a Gang (30)
  • 1107 Social Clusters (30)
  • 1118 Birds in Forest (25)

《算法笔记》 9.6 并查集 重点摘要

1. 定义

  • father[i] 表示元素 i的父结点
  • father[i] = i 表示元素 i 为该集合根结点
  • 每个集合只存在一个根结点,且其作为所属集合的标识
int father[N];

2. 初始化

最初每个元素都是一个独立的集合

for (int i = 0; i <= n; i++)
father[i] = i;

3. 查找

对给定结点寻找其根结点:反复寻找父结点直到根结点

  • 递推实现
int findFather (int x){
while (x != father[x])
x = father[x];
return x;
}
  • 递归实现
int findFather (int x){
if (x == father[x]) return x;
else return findFather(father[x]);
}

4. 合并

  • 判断两元素是否在同一集合

    • 分别查找两根结点
    • 判断根结点是否相同
  • 合并集合:其中一个根结点的父节点指向另一个根结点
  • 性质:只对不同集合进行合并,保证同一个集合中不会产生环,即并查集每个集合都是一棵树
void Union (int a, int b){
int faA = findFather(a);
int faB = findFather(b);
if (faA != faB) father[faA] = faB;
}

5. 路径压缩

将当前结点查询路径上所有结点的父节点都指向根节点

  • 递推实现
int findFather (int x){
int y = x; // 将初始查询结点记录下来
while (x != father[x]) // 用 x 进行回溯
x = father[x]; // 找到 x 对应根结点记录在 x 里
while (y != father[y]){ // 用记录的初始查询结点重新回溯
int z = y; //将此结点记录下来
y = father[y]; //向父结点回溯
father[z] = x; //将当前结点指向根结点
}
return x;
}
  • 递归实现
int findFather (int x){
if (x == father[x]) return x;
else{
int F = findFather(father[x]);
father[x] = F;
return F;
}
}

1034 Head of a Gang (30)

题目思路

  • 未作哈希函数,直接用 map<string,string> 存储父子关系,用 map<string,int> 存储结点对应参数
  • 输入时将通话时长记录在个人对应的权重 map 中
  • 若此人未出现过 (father.find(name1) == father.end()),将独立成一个新集合,将其加入 map 并进行初始化 father[name1] = name1
  • 若已经出现过,不必初始化,直接与另一个通话对象 Union 起来
  • 在 Union 函数中,首先查找两个人对应的头目
  • 每加入一个新人就会调用 Union 函数,也就会查找其头目
  • 查找头目时,要保证头目是这个集合中权重最高的
  • 那么每次找到其头目后,要比较头目与新人的权值,若新人权值大,应设置为新的头目
  • 这样每次 Union 得到的都是两个人对应团伙中权值最大的头目,将权值更大的作为新的头目
  • 边输入边处理于是输入完成后可找到每个人对应的头目,遍历一次将团伙总通话时长和总人数分别与头目对应存储起来
  • 最后按标准筛选出符合要求的团伙,放到 map 中
  • 先输出符合要求的团伙数量即对应 map.size() 再按要求输出团伙头目和团伙人数
#include<iostream>
#include<map>
using namespace std;
map<string,string> father;
map<string,int> weight, totaltime, membernum, head_num;
string findFather(string s){
string t = s;
while (father[s] != s) s = father[s];
if (weight[s] < weight[t]){
father[s] = t;
father[t] = t;
return t;
}
return s;
}
void Union(string s1, string s2){
string fa1 = findFather(s1);
string fa2 = findFather(s2);
if (weight[fa1] > weight[fa2]) father[fa2] = fa1;
else father[fa1] = fa2;
}
int main()
{
int n, k, time;
scanf("%d%d", &n, &k);
string name1, name2;
for (int i = 0; i < n; i++){
cin >> name1 >> name2 >> time;
weight[name1] += time;
weight[name2] += time;
if (father.find(name1) == father.end()) father[name1] = name1;
if (father.find(name2) == father.end()) father[name2] = name2;
Union(name1, name2);
}
for (auto it: father){
totaltime[findFather(it.first)] += weight[it.first];
membernum[findFather(it.first)]++;
}
for (auto it: membernum)
if (it.second > 2 && totaltime[it.first] / 2 > k) head_num[it.first] = it.second;
cout << head_num.size() << endl;
for (auto it: head_num) cout << it.first << " " << it.second << endl;
return 0;
}

简化前 findFather 函数

string findFather(string s){
while (father[s] != s){
string fa = father[s];
if (weight[fa] < weight[s]){
if (father[fa] == fa) father[s] = s;
else father[s] = father[fa];
father[fa] = s;
}
else s = father[s];
}
return s;
}

1107 Social Clusters (30)

题目思路

  • 用 set 数组保存每个人的兴趣爱好
  • 输入完成后遍历 set 数组,将每个与之前的人的爱好 set 有重合的进行合并
  • 合并处理完成后,遍历 father 数组,记录根结点即集合数,给每个结点对应根结点记录的集合人数 ++
  • 将 map 按值排序,成对放入 vector,写 cmp 函数实现,最后按要求输出
  • 注意:合并时,要对此人之前所有人的兴趣集合进行检查,不能检查到有相同的就停下,有三个点过不去。

    问题在于如果前面有一个人兴趣为{3},一个人兴趣为{5},现在这个人兴趣为{3,5},在处理这个人之前,前两个人并不会被放入同一个 cluster 中,只有将现在这个人与前两个人分别合并,他们三人才能进入同一个 cluster

#include<iostream>
#include<set>
#include<algorithm>
#include<vector>
#include<unordered_map>
using namespace std;
int father[1001];
set<int> hobbies[1001];
bool cmp (pair<int,int> a, pair<int,int> b){ return a.second > b.second; }
int findFather(int x){
while (x != father[x]) x = father[x];
return x;
}
void Union(int a, int b){
int faA = findFather(a);
int faB = findFather(b);
if (faA != faB) father[faA] = faB;
}
int main()
{
for (int i = 0; i < 1001; i++) father[i] = i;
int n, k, hobby, clusternum = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++){
scanf("%d: ", &k);
for (int j = 0; j < k; j++){
scanf("%d", &hobby);
hobbies[i].insert(hobby);
}
}
for (int i = 1; i < n; i++)
for (int j = 0; j < i; j++)
for (auto it: hobbies[i])
if (hobbies[j].find(it) != hobbies[j].end()) Union(i,j);
unordered_map<int,int> peoplenum;
for (int i = 0; i < n; i++){
if (father[i] == i) clusternum++;
peoplenum[findFather(i)]++;
}
vector<pair<int,int>> sortnum;
for (auto it: peoplenum) sortnum.push_back({it.first,it.second});
sort(sortnum.begin(),sortnum.end(),cmp);
printf("%d\n%d",clusternum,sortnum[0].second);
for (int i = 1; i < sortnum.size(); i++) printf(" %d",sortnum[i].second);
return 0;
}

1118 Birds in Forest (25)

题目思路

  • 鸟编号从 1 开始,father 数组大小要开到 10001,并查集初始化 father[i] = i
  • 每输入一幅画中鸟的数量,先接收输入第一只鸟的编号,再循环接收其他的鸟,将后输入的鸟与第一只鸟合并
  • 用 set 记录鸟的数量,每输入一只鸟就压入 set,set 会自动去重,输入完成后 set 的大小即为鸟的数量
  • 由于鸟编号连续,由 set 大小即可知 father 数组记录到了哪里
  • 遍历记录了鸟信息的部分 father 数组,找到 father[i+1] == i+1 的即为根节点,根结点树即为集合数也即为树的棵数
  • 最后看两只鸟是否在同一棵树,分别查询两只鸟的根结点,比较是否相等即可
#include<iostream>
#include<set>
using namespace std;
int father[10001];
int findFather(int x){
while (x != father[x]) x = father[x];
return x;
}
void Union(int a, int b){
int faA = findFather(a);
int faB = findFather(b);
if (faA != faB) father[faA] = faB;
}
int main()
{
for (int i = 0; i < 10001; i++) father[i] = i;
int n, k, q, q1, q2, fbird, bird, treenum = 0;
scanf("%d", &n);
set<int> birds;
for (int i = 0; i < n; i++){
scanf("%d%d", &k, &fbird);
birds.insert(fbird);
for (int j = 0; j < k - 1; j++){
scanf("%d", &bird);
birds.insert(bird);
Union(fbird,bird);
}
}
for (int i = 0; i < birds.size(); i++)
if (father[i+1] == i+1) treenum++;
printf("%d %d\n", treenum, birds.size());
scanf("%d", &q);
for (int i = 0; i < q; i++){
scanf("%d%d", &q1, &q2);
printf("%s\n", findFather(q1) == findFather(q2) ? "Yes" : "No");
}
return 0;
}

PAT甲级 并查集 相关题_C++题解的更多相关文章

  1. PAT甲级 图的遍历 相关题_C++题解

    图的遍历 PAT (Advanced Level) Practice 图的遍历 相关题 目录 <算法笔记>重点摘要 1021 Deepest Root (25) 1076 Forwards ...

  2. PAT甲级 Dijkstra 相关题_C++题解

    Dijkstra PAT (Advanced Level) Practice Dijkstra 相关题 目录 <算法笔记>重点摘要 1003 Emergency (25) <算法笔记 ...

  3. PAT甲级 二叉树 相关题_C++题解

    二叉树 PAT (Advanced Level) Practice 二叉树 相关题 目录 <算法笔记> 重点摘要 1020 Tree Traversals (25) 1086 Tree T ...

  4. PAT甲级 二叉查找树 相关题_C++题解

    二叉查找树 PAT (Advanced Level) Practice 二叉查找树 相关题 目录 <算法笔记> 重点摘要 1099 Build A Binary Search Tree ( ...

  5. PAT甲级 图 相关题_C++题解

    图 PAT (Advanced Level) Practice 用到图的存储方式,但没有用到图的算法的题目 目录 1122 Hamiltonian Cycle (25) 1126 Eulerian P ...

  6. PAT甲级 树 相关题_C++题解

    树 目录 <算法笔记>重点摘要 1004 Counting Leaves (30) 1053 Path of Equal Weight (30) 1079 Total Sales of S ...

  7. PAT甲级 堆 相关题_C++题解

    堆 目录 <算法笔记>重点摘要 1147 Heaps (30) 1155 Heap Paths (30) <算法笔记> 9.7 堆 重点摘要 1. 定义 堆是完全二叉树,树中每 ...

  8. PAT题解-1118. Birds in Forest (25)-(并查集模板题)

    如题... #include <iostream> #include <cstdio> #include <algorithm> #include <stri ...

  9. 【HDU1231】How Many Tables(并查集基础题)

    什么也不用说,并查集裸题,直接盲敲即可. #include <iostream> #include <cstring> #include <cstdlib> #in ...

随机推荐

  1. 【转载】Hadoop集群各部分常用端口号

    hadoop集群的各部分一般都会使用到多个端口,有些是daemon之间进行交互之用,有些是用于RPC访问以及HTTP访问.而随着hadoop周边组件的增多,完全记不住哪个端口对应哪个应用,特收集记录如 ...

  2. [WEB安全]phpMyadmin后台任意文件包含漏洞分析(CVE-2018-12613)

    0x00 简介 影响版本:4.8.0--4.8.1 本次实验采用版本:4.8.1 0x01 效果展示 payload: http://your-ip:8080/index.php?target=db_ ...

  3. mysql的 UUID的生成方式

    之前一直用的  int 自增的方式,之后总觉得缺少自信.  之后,我觉得采用uuid的方式,可能会好一些,至于用户统计排序等,则另用属性进行记录.   这里设计到一对矛盾:  安全性 与  网络带宽利 ...

  4. 关于大JSON 的问题的解决方式

    ASP.NET MVC JSON 大数据异常提示JSON 字符串超出限制的异常问题 今天客户突然过来找我说在后台添加了一篇超长的文章后,所有后台的文章都显示不出来了.后台的前端显示是用easyui的, ...

  5. 如何实现数组与List的相互转换

    List转数组:toArray(arraylist.size()方法 数组转List:Arrays的asList(a)方法 List<String> arrayList = new Arr ...

  6. 认识wsgi

    WSGI是什么? WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义 ...

  7. 设计自用的golang日志模块

    设计自用的golang日志模块 golang的原生日志模块不能满足需求,而开源的第三方包,也不完全够用.用户较多的logrus,却没有rotate功能,这已经是众所周知的.对于运维来说,当然是希望日志 ...

  8. ISO/IEC 9899:2011 条款6.4.9——注释

    6.4.9 注释 1.除了在一个字符常量.一个字符串字面量.或一个注释内,字符 /* 引入一个注释.这么一个注释的内容被检查仅用于标识多字节字符,并且要找到 */ 来终结.[注:从而,/* ... * ...

  9. 26 Flutter仿京东商城项目 购物车之 event_bus事件广播 事件监听

    event_bus 介绍 在前面的课程我们给大家讲过状态管理 Provider 的使用. 通俗的讲状态管理就是:当我们想在多个页面(组件/Widget)之间共享状态(数据),或 者一个页面(组件/Wi ...

  10. Laravel 项目开发环境配置

    1.首先安装Laravel 依赖管理包工具 Composer (前提是本地装好了PHP  php -v) php -r "copy('https://install.phpcomposer. ...