問題描述:
題目背景
AAA地區在地震過後,連接所有村莊的公路都造成了損壞而無法通車。政府派人修復這些公路。
題目描述
給出A地區的村莊數NNN,和公路數MMM,公路是雙向的。並告訴你每條公路的連着哪兩個村莊,並告訴你什麼時候能修完這條公路。問最早什麼時候任意兩個村莊能夠通車,即最早什麼時候任意兩條村莊都存在至少一條修復完成的道路(可以由多條公路連成一條道路)
輸入格式
第111行兩個正整數N,MN,MN,M
下面MMM行,每行333個正整數x,y,tx, y, tx,y,t,告訴你這條公路連着x,yx,yx,y兩個村莊,在時間t時能修復完成這條公路。
輸出格式
如果全部公路修復完畢仍然存在兩個村莊無法通車,則輸出−1-1−1,否則輸出最早什麼時候任意兩個村莊能夠通車。
輸入輸出樣例
輸入 #1
4 4
1 2 6
1 3 4
1 4 5
4 2 3
輸出 #1
5
說明/提示
N≤1000,M≤100000N \le 1000,M \le 100000N≤1000,M≤100000
x≤N,y≤N,t≤100000x \le N,y \le N,t \le 100000x≤N,y≤N,t≤100000
基本思路:
利用並查集合並集合。
這裏除第一行後的每一行都是關於邊的信息,每一條邊連接哪兩個節點,這種題目我們就可以用並查集來做。
唯一的問題就是如何判斷我們的集合是否已經包含全部的元素。
這裏我的解決方法就是引入一個記錄根節點所含元素個數的變量,如果他的個數==n,那麼就說明已經完全連同了。
AC代碼:
#include<bits/stdc++.h>
using namespace std;
struct Road {
// 兩個村莊的編號以及修復路的時間
int x;
int y;
int t;
Road() = default;
Road(int _x, int _y, int _t) :
x(_x), y(_y), t(_t) {}
};
constexpr int kNum = 1005;
int p[kNum]; // 存放父親節點
int r[kNum]; // 存放集合的秩
int s[kNum]; // 存放集合的元素個數
// 這裏竟然也要提供構造函數
vector<Road> road; // 這裏的容器好像就是某種特定類型的容器
void Initialize() {
for (int i = 0; i < kNum; ++i) {
p[i] = i;
r[i] = s[i] = 1;
}
}
int Find(int x) {
// 根據元素找相應集合的根節點
// 找到根節點了
if (x == p[x]) {
return x;
}
return p[x] = Find(p[x]);
}
void Union(int x, int y) {
int p_x = Find(x);
int p_y = Find(y);
// 兩個元素位於同一個集合中
if (p_x == p_y) {
return;
}
// 兩個元素不在同一個集合中
if (r[p_x] > r[p_y]) {
p[p_y] = p_x;
s[p_x] += s[p_y];
} else {
if (r[p_x] == r[p_y]) {
++r[p_y];
}
p[p_x] = p_y;
s[p_y] += s[p_x];
}
}
int main() {
Initialize();
int n, m;
cin >> n >> m;
for (int i = 0; i < m; ++i) {
int q, w, e;
cin >> q >> w >> e;
road.push_back(Road(q, w, e));
}
sort(road.begin(), road.end(), [] (Road x, Road y) { return x.t < y.t; });
for (int i = 0; i < m; ++i) {
Union(road[i].x, road[i].y);
if (s[Find(road[i].x)] == n) {
cout << road[i].t << endl;
return 0;
}
}
cout << "-1\n";
return 0;
}
其他經驗:
- 並查集在使用之前要進行初始化。
- 自定義類的數組,由於不知道可能要放什麼元素,所以一般不會進行初始化,這時候就要求你提供默認構造函數了。
- 要明白自己是使用vector還是使用array,後者可能由於不知道會有幾個元素,所以預先開闢很多空間——這樣就會導致很多沒有用的元素會混到我們所需要元素的操作中(比如對數組進行sort排序,那隻能begin, end, 但這個顯然不對);vector使用效率可能比array的效率要稍微低一點(開闢新的空間啥的),但是就沒有這樣的困擾。