給定有向邊序列,判斷是否構成一棵樹。樹的定義:有且僅有一個根(入度 0)、其餘節點入度為 1、無環、連通。
#include <bits/stdc++.h>
using namespace std;
struct UnionFind {
vector<int> p, r;
UnionFind(int n) : p(n), r(n, 0) { iota(p.begin(), p.end(), 0); }
int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
bool unite(int a, int b) {
a = find(a); b = find(b);
if (a == b) return false;
if (r[a] < r[b]) swap(a, b);
p[b] = a;
if (r[a] == r[b]) r[a]++;
return true;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int u, v, case_no = 1;
while (cin >> u >> v) {
if (u == -1 && v == -1) break;
vector<pair<int,int>> edges;
set<int> nodes;
bool is_tree = true;
if (u == 0 && v == 0) { // 空樹
cout << "Case " << case_no++ << " is a tree.\n";
continue;
}
edges.emplace_back(u, v);
nodes.insert(u); nodes.insert(v);
if (u == v) is_tree = false;
while (cin >> u >> v) {
if (u == 0 && v == 0) break;
edges.emplace_back(u, v);
nodes.insert(u); nodes.insert(v);
if (u == v) is_tree = false;
}
if (!is_tree) {
cout << "Case " << case_no++ << " is not a tree.\n";
continue;
}
// 離散化
unordered_map<int,int> id;
int idx = 0;
for (int x : nodes) id[x] = idx++;
UnionFind uf(idx);
vector<int> in_deg(idx, 0);
for (auto [a, b] : edges) {
int u = id[a], v = id[b];
if (!uf.unite(u, v)) { is_tree = false; break; }
in_deg[v]++;
}
if (!is_tree) {
cout << "Case " << case_no++ << " is not a tree.\n";
continue;
}
// 檢查入度:恰好一個 0,其餘 1
int root_count = 0;
for (int d : in_deg) {
if (d == 0) root_count++;
else if (d > 1) { is_tree = false; break; }
}
if (root_count != 1) is_tree = false;
// 檢查連通性
int root = uf.find(0);
for (int i = 1; i < idx; ++i)
if (uf.find(i) != root) { is_tree = false; break; }
cout << "Case " << case_no++ << (is_tree ? " is a tree." : " is not a tree.") << "\n";
}
return 0;
}
| 情況 | 處理 |
|---|---|
| 空輸入 (0 0) | 直接判樹 |
| 自環 u→u | 非樹 |
| 重複邊 | 並查集檢測到環 |
| 多根/無根 | 入度統計 |
| 不連通 | 並查集根計數 > 1 |
| 節點編號不連續 | 離散化映射 |
也可用 Kahn 算法:入度為 0 的節點入隊,拓撲排序成功且處理節點數 = 總節點數 ⇒ 是樹。但並查集更直觀易實現。
此題是並查集判斷有向圖是否為樹的模板題,綜合考察入度統計、環檢測、連通性三大核心條件。