#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//并查集(DSU)结构体
struct DSU{
vector<int> parent;//核心数组,记录每个点的祖宗
//构造函数:对象诞生时自动调用!
DSU(int n) : parent(n+1){//初始化列表:直接创建大小为n+1的vector
for(int i = 1 ; i<=n ; i++)
parent[i] = i;//每个节点初始的祖宗是自己
//此时parent数组实例(n=3):[?,1,2,3] (?表示未使用的parent[0])
}
//查找函数 :找x的终极祖宗+路径压缩
int find(int x) {
if(parent[x]!=x)//如果x不是祖宗
parent[x] = find(parent[x]);
return parent[x];
}
//合并函数:把x和y所在的家族合并
bool unionSets(int x , int y) {
int xRoot = find(x);//找到x的祖宗
int yRoot = find(y);//找到y的祖宗
if(xRoot==yRoot) return false;//本来就是一家的,不用合并
parent[yRoot] = xRoot;//让y的祖宗认x的祖宗当亲爹
return true;//返回合并成功
}
};
//Kruskal 算法主函数
int Kruskal(int n , vector<vector<int>>& edges) {
//1.贪心核心,所有边按权值从大到小排序
sort(edges.begin(),edges.end(),[](const vector<int> &a,const vector<int> &b) {
return a[2]<b[2];//比较每条边的第三个元素(权值)
});
DSU dsu(n) ;
int totalWeight = 0;//记录最小生成树的总权值
int edgesUsed = 0;//记录已使用的边数(目标:n-1条)
//2.遍历排序后的所有边
for(const auto&edge:edges) {
int u = edge[0];//边的起点
int v = edge[1];//边的终点
int w = edge[2];//边的权值
//3.尝试合并u和v所在的集合
if(dsu.unionSets(u,v)) {//如果合并成功(未成环)
totalWeight += w;//累加当前权值
edgesUsed++;//已用边数+1
if(edgesUsed==n-1) break;//够数了,提前退出
}
}
//4.检查是否联通(边数是否够n-1)
return (edgesUsed==n-1)?totalWeight:-1;
}
//main函数(程序入口)
int main()
{
//输入部分
int n , m;//n=顶点数,m=边数
cin>>n>>m;
vector<vector<int>> edges(m,vector<int>(3)) ;//创建m行三列的二维数组
for(auto i = 0 ; i<m ; i++)
cin>>edges[i][0]>>edges[i][1]>>edges[i][2];//读每条边的u,v,w
int result = Kruskal(n,edges) ;
if(result==-1)
cout<<"orz"<<endl;
else
cout<<result<<endl;
return 0;
}
洛谷P3336【模版】最小生成树 题解(kruskal版)
于 2025-08-10 10:54:26 首次发布

643

被折叠的 条评论
为什么被折叠?



