洛谷: P1656 炸铁路(并查集)
今天记录一道并查集的题目。
思路: 枚举每一条铁路,假设去掉这条铁路,检测图中的n个地点是否还能连通。如果不能连通,说明需要炸掉这条路,如果能连通,说明去掉这条路并不影响连通性,就没必要把这条路炸了。
注意Union方法,如果x和y的根节点是一样的,直接return,否则就成环了。
注:vector中pair的默认排序是第一个从小往大排,第一个一样的情况下按照第二个排。所以就用的vector的pair,没用结构体。(因为我对C++不太熟悉,所以就注明一下这个问题)
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 155;
const int maxm = 5005;
int ranks[maxn], parent[maxn], n, m;
vector<pair<int, int>> edges;
void init() {
for (int i = 1; i <= n; i++) {
parent[i] = -1;
ranks[i] = 0;//高度
}
}
int find(int x) {
int x_root = x;
while (parent[x_root] != -1)
x_root = parent[x_root];
return x_root;
}
void Union(int x,int y) {
int x_root = find(x);
int y_root = find(y);
if (x_root == y_root) return;
if (ranks[x_root] < ranks[y_root]) {
parent[x_root] = y_root;
}
else if (ranks[x_root] > ranks[y_root]) {
parent[y_root] = x_root;
}
else {
parent[x_root] = y_root;
ranks[y_root]++;
}
}
int main() {
cin >> n >> m;//m个铁路
edges.resize(m + 1);
for (int i = 0; i < m; i++) {
cin >> edges[i].first >> edges[i].second;
if (edges[i].first > edges[i].second) swap(edges[i].first, edges[i].second);
}
sort(edges.begin(), edges.begin()+m);
//枚举去掉某条道路 将剩下的合并
for (int i = 0; i < m; i++) {
init();
for (int j = 0; j < m; j++) {//合并剩下的
if (j != i) {
Union(edges[j].first, edges[j].second);
}
}
//检测有几个祖先
bool flag = true;
int par = find(1);//结点1
for (int j = 2; j <= n; j++) {
if (find(j) != par) {
flag = false;
break;
}
}
if (!flag) {
cout << edges[i].first << " " << edges[i].second << endl;
}
}
return 0;
}