
对该算法程序编写以及踩坑点很熟悉的同学可以直接跳转到代码模板查看完整代码
只有基础算法的题目会有关于该算法的原理,实现步骤,代码注意点,代码模板,代码误区的讲解
非基础算法的题目侧重题目分析,代码实现,以及必要的代码理解误区
给定一个 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≤105,
1≤m≤2∗105,
图中涉及边的边权的绝对值均不超过 1000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
题目来源:https://www.acwing.com/problem/content/861/
边数m = 2*105,点数n = 105,稀疏无向图
自环,负边权,重边,均对于最小生成树无影响
专攻最小生成树问题含有两大算法:从点出发的Prim 和 从边出发的Kruskal
Prim算法时间复杂度O(n2),最坏耗时1010,远超108
Kurskal算法时间复杂度O(mlogm),最坏耗时106
下面来讲由并查集实现,从图中边出发的kruskal算法
边的存储:
边以结构体数组方式存储,便于调用sort()函数
每个边元素含有三个属性:起点a,终点b,边长w
最小生成树的存储:
用并查集方式存储最小生成树
最终经过n -1次合并,将n-1个点合并在了一棵树上,所有点的fa[]都是一个点
若两点a b的fa[]不相同,则他们不在一个最小生成树上,需要将各自所在的树通过最短边连接起来
res存储最小生成树内各边长之和
cnt记录最小生成树内含有的边数,也就是经过了多少次集合合并
从结果出发思考:
最终图中所有点都连接进入了最小生成树
而每个点都是通过边连接进入最小生成树中的
所以只要每个点进入最小生成树时,借助的边的边长最短,最终生成的树就是最小生成树
三大步骤:
将所有边按照权重由小到达排序O(mlogm):
使用STL的sort()即可,但是边类需要重构operator <
枚举每条边:起点a,终点b,权重w
如果a b不在同一个集合中(即一个树上,一个非树),将ab连接到同一个集合中(全部入非树也就是全部是树了)。
比较cnt和n-1,cnt小于n-1时说明最小生成树连接不满n个点,此图不是连通图。




从短到长遍历每一条边,
若边的起点终点不连通,则该边就是连通两棵子树成为最小生成树的最短边长
若边的起点终点连通,则两点都在最小生成树中
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 200020;
struct Edge{
int a, b ,w;
bool operator<(const Edge &W) const{
return w < W.w;
}
}edge[M]; //易错点:存储边的数目使用M
int n, m;
int fa[N];
void init(){
for(int i=1; i<=n; i++) fa[i] = i;
}
int find(int x){
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
int main(){
cin >>n >>m;
for(int i=0; i<m; i++){
cin >>edge[i].a >> edge[i].b >>edge[i].w;
}
sort(edge, edge+m);
init();
int cnt = 0, res = 0;
for(int i=0; i<m; i++){
int a = edge[i].a;
int b = edge[i].b;
a = find(a);
b = find(b);
if (a != b){
cnt++;
res += edge[i].w;
fa[a] = b;
}
}
if (cnt == n-1) cout<<res;
else cout<<"impossible"<<endl;
}
优先选择最短边,将所有点连接到最小生成树集合中
结构体数组解决边的问题,fa[]并查集数组解决点的问题
从遍历边出发,将所有点连接为最小生成树
const int N = 100010;
//一般使用的数组大小N是点的数目
//本题结构体为边,结构体数组大小应该是边数M
const int M = 100010;
struct Edge{
int a, b, w;
bool operator< (const Edge &W) const{
return w < W.w;
}
}edge[M];
//由于边数大于点数,所以继续采用N作数组长度容易内容越界

距离登仙境不远了,加油 