洛谷P1195题:最小生成树与Kruskal算法实践
想象你面前有N朵独立的云彩,每朵云都可以通过特定代价与其他云相连。你的任务是将这些云连成K个"棉花糖"(即K个连通分量),每个棉花糖至少包含一朵云,且总连接代价最小。这与经典的图论问题——最小生成树(MST)非常相似,但有一个关键区别:我们不需要将所有节点连成一棵树,而是需要形成K棵树(称为最小生成森林)。
一、算法选择
Kruskal算法特别适合解决这个问题,因为:
二、完整代码
#include <iostream> #include <algorithm> #include <vector> using namespace std; // 定义边的结构体 struct Edge { int x, y, l; // x和y表示云朵编号,l表示连接代价 bool operator<(const Edge& other) const { return l < other.l; // 按代价从小到大排序 } }; vector<Edge> edges; // 存储所有边 vector<int> parent; // 并查集数组 // 并查集查找根节点 int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); // 路径压缩 } return parent[x]; } int main() { int N, M, K; cin >> N >> M >> K; // 初始化并查集 parent.resize(N+1); for (int i = 1; i <= N; ++i) { parent[i] = i; } // 读取所有边 edges.resize(M); for (int i = 0; i < M; ++i) { cin >> edges[i].x >> edges[i].y >> edges[i].l; } // 按边权从小到大排序 sort(edges.begin(), edges.end()); int totalCost = 0; int connectedComponents = N; // 初始时每个云朵自成一个连通分量 // Kruskal算法构建最小生成森林 for (const auto& e : edges) { if (connectedComponents <= K) break; // 已经形成K个连通分量 int rootX = find(e.x); int rootY = find(e.y); if (rootX != rootY) { // 如果不在同一个连通分量 parent[rootY] = rootX; // 合并 totalCost += e.l; connectedComponents--; // 连通分量减少1 } } // 检查是否能形成K个棉花糖 if (connectedComponents == K) { cout << totalCost << endl; } else { cout << "No Answer" << endl; } return 0; }
三、代码详解
数据结构准备
使用结构体
Edge
存储每条边的信息parent
数组用于并查集操作edges
向量存储所有输入的边并查集初始化
初始时每个云朵自成一个连通分量
find
函数实现了路径压缩,提高查询效率核心算法流程
将边按权值排序
依次处理每条边,合并不同连通分量
当连通分量数等于K时停止
累计连接代价
结果验证
检查最终连通分量数是否恰好为K
输出总代价或"No Answer"
四、常见错误与调试技巧
连通分量计数错误
初始值应为N(每个节点独立)
每次成功合并后减1
当K>N时直接输出"No Answer"
当K=1时退化为标准MST问题
性能优化
输入规模较大时使用快速IO
确保并查集实现高效(路径压缩)
原创内容 转载请注明出处