前言
1.并查集用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
a.合并(Union):把两个不相交的集合合并为一个集合。
b.查询(Find):查询两个元素是否在同一个集合中。
2.路径压缩
对于一些指向较长的并查集,(1->2->3->.....->n)它的合并和查找效率极慢(O(N)),而并查集本身并不关注各个集合之间具体的联通关系,换言之,以下两者在运用并查集处理问题时等效
而后者的合并,查询操作的时间仅为O(1);
(自用,相关基础操作就不赘述了)
一、P1551 亲戚
题目描述
规定:x 和 y 是亲戚,y 和 z 是亲戚,那么 x 和 z 也是亲戚。如果 x,y 是亲戚,那么 x 的亲戚都是 y 的亲戚,y 的亲戚也都是 y 的亲戚。
输入格式
第一行:三个整数 n,m,p,(n,m,p≤5000),分别表示有 n 个人,m 个亲戚关系,询问 p 对亲戚关系。
以下 m 行:每行两个数Mi,Mj,1≤Mi, Mj≤n,表示 Mi 和 Mj 具有亲戚关系。
接下来 p 行:每行两个数 Pi,Pj,询问 Pi 和 Pj 是否具有亲戚关系。
输出格式
p 行,每行一个 Yes
或 No
。表示第 i 个询问的答案为“具有”或“不具有”亲戚关系。
输入输出样例
输入
6 5 3 1 2 1 5 3 4 5 2 1 3 1 4 2 3 5 6
输出
Yes Yes No
题目分析
并查集模板题,并查集相关操作见代码
代码样例
#include <stdio.h>
const int N = 1e5 + 10;
int pre[N];
int root(int x) {
return pre[x] = (pre[x] == x ? x : root(pre[x])); //包含路径压缩的find操作
}
int main() {
int n, m, p;
int t1,t2;
scanf("%d%d%d", &n, &m, &p);
for(int i = 1; i <= n; i++) pre[i] = i;
for(int i = 1; i <= m; i++) {
scanf("%d%d", &t1, &t2);
pre[root(t1)] = root(t2); //合并(merge)操作,可单独写函数外置
}
for(int i = 1; i <= p; i++) {
scanf("%d%d", &t1, &t2);
if(root(t1) == root(t2)) printf("Yes\n"); //如果根节点(祖先)相同,则为亲戚
else printf("No\n");
}
return 0;
}
二、P1111 修复公路
题目描述
给出 A 地区的村庄数 N,和公路数 M,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)。
输入格式
第 1 行两个正整数 N,M。
下面 M 行,每行 3 个正整数 x,y,t,告诉你这条公路连着 x,y 两个村庄,在时间t时能修复完成这条公路。
输出格式
如果全部公路修复完毕仍然存在两个村庄无法通车,则输出 −1,否则输出最早什么时候任意两个村庄能够通车。
输入输出样例
输入
4 4 1 2 6 1 3 4 1 4 5 4 2 3
输出
5
说明/提示
1≤x,y≤N≤,1≤M,t≤。
题目分析
与模板题不同的是,本题在连接时要考虑应时间而产生的连接顺序;那么可以先根据时间对待连接区块进行排序,再根据时间遍历,进行连通操作。而连通时,由于每两个村庄间可能不止一条道路,对于这些多余的道路不妨直接跳过(对所求结果无影响);而当有效通路数量为 n-1 时,所有村庄便已悉数联通。
代码示例
#include <cstdio>
#include <iostream>
#include <algorithm>
const int N = 1e5 + 10;
struct node{
int x, y, t;
}tem[N];
int pre[N];
int t1, cnt;
int cmp(const node &a,const node &b) {
return a.t < b.t;
}
int root(int x) {
return pre[x] = (pre[x] == x ? x : root(pre[x]));
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) pre[i] = i;
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &tem[i].x, &tem[i].y, &tem[i].t);
}
std::sort(tem + 1, tem + m + 1, cmp);
for(int i = 1; i <= m; i++) {
if(root(tem[i].x) != root(tem[i].y)) { //如果两者之前未联通
pre[root(tem[i].x)] = root(tem[i].y);
n--;
}
if(n == 1) {
printf("%d\n", tem[i].t);
return 0;
}
}
printf("-1\n");
return 0;
}