最小生成树.pdf
最小生成树
我们可以使用 Kruskal 算法或 Prim 算法来求解图的最小生成树。这里选择使用 Kruskal 算法,它的思想是按边的权重从小到大排序,然后逐步添加边到最小生成树中,保证添加的每条边不会形成环,最终得到一个包含所有节点的生成树。
步骤:
- 输入解析:读取节点数
N
和边数 M
,接着读取每条边的节点对和权重。
- 排序:将所有边按照权重从小到大排序。
- 并查集:使用并查集(Union-Find)来管理图中的连通性,确保在添加一条边时不会形成环。
- 逐步添加边:从最小的边开始,如果这条边连接的两个节点属于不同的连通块,则将其加入生成树,并合并这两个连通块。直到生成树包含
N-1
条边。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #include <iostream> #include <vector> #include <algorithm> using namespace std;
class UnionFind { public: UnionFind(int n) { parent.resize(n); size.resize(n, 1); for (int i = 0; i < n; i++) { parent[i] = i; } }
int find(int u) { if (parent[u] != u) { parent[u] = find(parent[u]); } return parent[u]; }
void unionSets(int u, int v) { int rootU = find(u); int rootV = find(v);
if (rootU != rootV) { if (size[rootU] < size[rootV]) { parent[rootU] = rootV; size[rootV] += size[rootU]; } else { parent[rootV] = rootU; size[rootU] += size[rootV]; } } }
private: vector<int> parent; vector<int> size; };
struct Edge { int u, v, w; bool operator<(const Edge& other) const { return w < other.w; } };
int main() { int N, M; cin >> N >> M; vector<Edge> edges(M);
for (int i = 0; i < M; i++) { cin >> edges[i].u >> edges[i].v >> edges[i].w; }
sort(edges.begin(), edges.end());
UnionFind uf(N); int mstWeight = 0; int edgeCount = 0;
for (const auto& edge : edges) { if (uf.find(edge.u) != uf.find(edge.v)) { uf.unionSets(edge.u, edge.v); mstWeight += edge.w; edgeCount++; if (edgeCount == N - 1) { break; } } }
cout << mstWeight << endl;
return 0; }
|
解释:
- UnionFind 类:实现了并查集的数据结构,支持
find
(查找根节点)和 unionSets
(合并两个节点的连通块)。
- Edge 结构体:每个边包含两个节点和该边的权重,并重载了
<
操作符,以便可以按权重排序。
- 主函数:
- 先读取所有边的输入并存储在
edges
向量中。
- 将边按权重升序排序。
- 使用并查集处理每条边,判断其是否可以加入到最小生成树中(即它的两个端点是否属于不同的连通块)。
- 当已加入的边数为
N-1
时,停止算法。
- 输出最小生成树的总权重。
复杂度:
- 时间复杂度:排序的时间复杂度为
O(M log M)
,每次并查集的 find
和 union
操作平均时间复杂度为 O(α(N))
(α 是反阿克曼函数,几乎是常数),因此总时间复杂度是 O(M log M + M α(N))
。在最坏情况下,M ≤ 100
和 N ≤ 100
,所以效率足够满足题目要求。
测试样例:
输入:
1 2 3 4 5
| 4 4 0 1 1 1 2 2 2 3 3 0 3 4
|
输出:
解释:
最小生成树包含的边是:
- 边 (0, 1) 权重 1
- 边 (1, 2) 权重 2
- 边 (2, 3) 权重 3
总权重为 1 + 2 + 3 = 6。