洛谷P1195题解析:Kruskal算法构建K个连通分量的优化解法
一、题目解读
洛谷P1195题要求处理一个图论问题:给定N朵云和M条边,每条边连接两朵云并附带代价,需通过添加边将云朵分组,使得最终形成K个连通分量,且总代价最小。若无法恰好形成K个连通分量,则输出“No Answer”。题目核心在于利用最小生成树的思想,在限定连通分量数量下求解最优解。
二、解题思路
采用Kruskal算法与并查集的结合。首先对所有边按代价升序排序,随后通过并查集维护连通分量。遍历每条边时,若合并后能减少连通分量数量且不超出K的限制,则加入该边并累加代价。关键在于利用路径压缩优化并查集查找,降低时间复杂度。
三、解题步骤
1. 输入云朵数N、边数M、目标连通分量数K。
2. 初始化并查集数组(每个云朵自成连通分量)。
3. 读取所有边存入结构体中,按代价排序。
4. 循环遍历边:
○ 若当前连通分量数已≤K,跳出循环。
○ 通过find()函数查找两云朵的根节点,若不同则合并,更新总代价及连通分量数。
5. 检查最终连通分量数是否等于K,输出总代价或“No Answer”。
四、代码及注释
#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; }
五、总结
该解法通过Kruskal算法的贪心策略,确保每次合并边时总代价最小,同时利用并查集高效维护连通性。时间复杂度O(ElogE)由排序主导,适用于稀疏图场景。当目标连通分量数K较小时,该算法能快速得到最优解,是解决此类问题的经典思路。
原创内容 转载请注明出处