原题:
题目背景
A 地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车。政府派人修复这些公路。
题目描述
给出 A 地区的村庄数 N N N,和公路数 M M M,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)。
输入格式
第
1
1
1 行两个正整数
N
,
M
N,M
N,M。
下面
M
M
M 行,每行
3
3
3 个正整数
x
,
y
,
t
x,y,t
x,y,t,告诉你这条公路连着
x
,
y
x,y
x,y 两个村庄,在时间t时能修复完成这条公路。
输出格式
如果全部公路修复完毕仍然存在两个村庄无法通车,则输出 − 1 -1 −1,否则输出最早什么时候任意两个村庄能够通车。
数据范围
1 ≤ x , y ≤ N ≤ 1 0 3 1\leq x, y\leq N \le 10 ^ 3 1≤x,y≤N≤103, 1 ≤ M , t ≤ 1 0 5 1\leq M, t \le 10 ^ 5 1≤M,t≤105。
算法:
- 并查集
- 排序
思路:
- 题目没有提及,但看样例可知,所有公路是同时开工的。
- 我们按时间排序,先判断时间用的更少的。
- 将两个相连的村庄放入同一个集合。
- 若联通集合中元素数目等于村庄数,则所有村庄已联通,输出现在的时间即可。
- 若修完所有的路,集合中元素数仍然小于村庄数,则说明无法所有村庄实现联通。
实现:
排序:
因为对
t
t
t 排序, 要
x
,
y
x, y
x,y 随
t
t
t 一起移动,可使用结构体
(或者使用pair<int, pair<int, int> >
f
i
r
s
t
存储
t
,
s
e
c
o
n
d
存储
x
,
y
first存储 t,second存储x,y
first存储t,second存储x,y )
元素数统计:
定义 size[]
数组
- 全部初始化为 1 1 1。
- 如果两个村庄合并,则将新集合的祖宗节点的
size[]
大小更改为原先两个集合的元素数之和。
实例代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl "\n"
//#define int long long
const int N = 100005; // 1e6+5
struct LGY {
int x, y, t;
};
// 使sort按t排序
bool cmp (LGY a, LGY b) {
return a.t < b.t;
}
// p数组存储父节点, siz数组存储集合元素个数
int p[N], siz[N];
vector<LGY> a;
// 查找祖宗节点函数,并路径压缩
int find(int x) {
return x == p[x] ? p[x] : p[x] = find(p[x]);
}
signed main(void) {
std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n, m, dx, ans = 0;
LGY z;
cin>> n >> m;
// 初始化p数组和siz数组
for(int i = 1; i <= n; i++)
p[i] = i, siz[i] = 1;
// 输入
for(int i = 1; i <= m; i++) {
cin>> z.x >> z.y >> z.t;
a.push_back(z);
}
// 排序
sort(a.begin(), a.end(), cmp);
// 遍历每条路
for(auto i:a) {
//如果 x, y 未联通
if(find(i.x) != find(i.y)) {
// 将 x, y 集合的元素数相加
siz[find(i.y)] += siz[find(i.x)];
// 将 x 集合的祖宗节点指向 y 集合的祖宗节点
p[find(i.x)] = find(i.y);
// 记录元素个数
dx = siz[find(i.y)];
// 更新答案
ans = i.t;
}
// 如果所有村庄联通则脱出循环
if(dx == n) break;
}
// 判断是否所有村庄联通
if(dx == n) cout<< ans ;
else cout<< -1;
return 0;
}