一、问题理解与算法选择这道题目描述了一个典型的图论问题:在保证所有节点连通的前提下,选择边的集合使得总成本最小,同时关注所选边中的最大值。这正是**最小生成树(MST)**问题的变形,我们可以使用Kruskal或Prim算法解决。由于题目需要找出最长的那根木材,Kruskal算法更为合适,因为它能按边权排序,方便记录最大边。 二、完整C++代码实现- #include <iostream>
- #include <vector>
- #include <algorithm>
- using namespACe std;
- // 并查集数据结构用于检测环路
- struct UnionFind {
- vector<int> parent;
-
- UnionFind(int n) : parent(n+1) {
- for(int i = 1; i <= n; ++i)
- parent[i] = i;
- }
-
- int find(int x) {
- if(parent[x] != x)
- parent[x] = find(parent[x]);
- return parent[x];
- }
-
- bool unite(int x, int y) {
- int rootX = find(x);
- int rootY = find(y);
- if(rootX == rootY) return false;
- parent[rootY] = rootX;
- return true;
- }
- };
- struct Edge {
- int u, v, weight;
- bool operator<(const Edge& other) const {
- return weight < other.weight;
- }
- };
- 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].weight;
- }
-
- // Kruskal算法核心步骤
- sort(edges.begin(), edges.end());
- UnionFind uf(N);
- int maxWood = 0, edgeCount = 0;
-
- for(const auto& e : edges) {
- if(uf.unite(e.u, e.v)) {
- maxWood = max(maxWood, e.weight);
- if(++edgeCount == N - 1) break;
- }
- }
-
- cout << (edgeCount == N - 1 ? maxWood : -1) << endl;
- return 0;
- }
复制代码
三、算法详解与优化并查集(Union-Find)数据结构:
Kruskal算法流程:
将所有边按权值升序排序(O(M log M)) 依次尝试加入每条边,使用并查集检测是否形成环路 当选中N-1条边时即完成最小生成树构建
空间复杂度:
四、新手学习指南理解图的基本概念:
顶点(空地)、边(木材)、权重(木材长度) 连通性要求(所有空地必须可达)
最小生成树的核心思想:
选择边的子集使图连通且总权重最小 无环路且包含所有顶点(N-1条边)
Kruskal算法的直观理解:
调试技巧:
五、常见问题解答Q: 为什么不能直接取所有边中最小的M-1条? A: 这样可能无法保证图的连通性,必须确保选择的边能连接所有顶点。 Q: 如何处理不连通图的情况? A: 代码中通过edgeCount检查,如果最终不足N-1条边,输出-1(题目保证连通故未处理)。 Q: 为什么使用并查集而不是DFS检测环路? A: 并查集的时间复杂度更优,特别适合动态连通性检查。
|