题目描述
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图$G=(V,E)$
其中$V$表示图中点的集合,$E$表示图中边的集合,$n=|V|$ ,$m=|E|$ 。
由 $V $ 中的全部 $ n $个顶点和$ E $ 中 $n−1$ 条边构成的无向连通子图被称为 $G$ 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 $ G$ 的最小生成树。
输入格式
第一行包含两个整数 n 和 m 。
接下来 m 行,每行包含三个整数 u,v,w
,表示点 u 和点 v 之间存在一条权值为 w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
$1≤n≤10^5$ ,
$1≤m≤2∗10^5$
图中涉及边的边权的绝对值均不超过 10000 。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
Kruskal $O(mlogn)$ 用于稀疏图
Kruskal算法通过加边的方式来进行最小生成树的构造。
类似于bellman-Ford不需要存储图,只需要存储边。题目中根据数据范围可看出,点的数目和边的数目大致相等,即稀疏图,故而采用kruskal算法。
知识概要
模板
流程
//存储每一条边,并且根据边的权值从小到大排序sort(struct)
//遍历每一条边
for(m){
如果a和b没有联通{ //通过并查集维护
把a,b联通
res+=c,cnt++; //cnt:多少点连通
}
}
if(cnt!=n-1)说明没有最小生成树
else cout<<res;
模板
//初始化并查集数组
init();
//输入每一条边
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
Ns[i]={a,b,c};
}
//对边进行从小到大排序
sort(Ns,Ns+m,cmp);
//遍历每一条边
for(int i=0;i<m;i++){
int a=Ns[i].a,b=Ns[i].b,c=Ns[i].c;
//判断a,b是否连通,如果不连通则将其连通,更新答案以及连通点数
if(find(a)!=find(b)){
res+=c;
merge(a,b);
cnt++;
}
}
//通过连通点数判断是否是最小连通树
if(cnt!=n-1) cout<<"impossible";
else cout<<res<<endl;
题解
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e5+10;
struct Node{
int a,b,c;
}Ns[M];
int fa[M],n,m,res,cnt;
void init(){
for(int i=0;i<n;i++){
fa[i]=i;
}
}
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void merge(int a,int b){
fa[find(a)]=find(b);
}
//从小到大排
bool cmp(Node x,Node y){
return x.c<y.c;
}
int main(){
cin>>n>>m;
init();
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
Ns[i]={a,b,c};
}
sort(Ns,Ns+m,cmp);
for(int i=0;i<m;i++){
int a=Ns[i].a,b=Ns[i].b,c=Ns[i].c;
if(find(a)!=find(b)){
res+=c;
merge(a,b);
cnt++;
}
}
if(cnt!=n-1) cout<<"impossible";
else cout<<res<<endl;
return 0;
}
⭐⭐
排序方法函数
//从小到大排
bool cmp(Node x,Node y){
return x.c<y.c;
}