最小生成树
给定一个图 用
条边将点连起来,得到一颗边权之和最小的树。
最小生成树有两种算法,用哪种取决于 大小,边比较稀疏用
完全图或者稠密图用
Prim算法
将图中的点分为两个集合一个表示在生成树中的点,一个表示不在生成树的点。
开始所有点都在 任选一个起点
加入
记录一个
数组表示
中的点到
中的点的边权最小值,每次选
值最小的点加入
,直到得到生成树。
复杂度
inline void Prim(){
memset(dis,0x3f3f3f,sizeof(dis));
int tot = 0;//tot记录生成树边权之和
vis[s] = 1;//vis 记录是否在G1中
dis[1] = 0;
int mini,k;
for(int i=1;i<=n;++i)dis[i] = f[s][i]; // 边权,若没有则是正无穷
for(int i=1;i<n;++i){
mini = 0x3f3f3f;
k = 0;
for(int j=1;j<=n;++j){//找到dis最小的点
if(!vis[j] && dis[j] < mini){
mini = dis[j];
k = j;
}
}
tot += dis[k];
vis[k] = 1;
for(int j=1;j<=n;++j)
if(f[k][j] < dis[j])
dis[j] = f[k][j];//更新边权最小值
}
}
Kruskal算法
该算法复杂度为
为边数
实现也比较简单好理解:
将边按照边权升序排列,每次取出权值最小的边,如果边的端点不在同一颗树就插入生成树,反之舍弃,直到插入条边。
是否在同一颗树用并查集维护。
inline int find(int x){return f[x] == x ? x : f[x] = find(f[x]);}
inline void kruskal(){
sort(g1+1,g1+1+m);//边权升序
for(int i=1;i<=n;++i)f[i] = i;
for(int i=1;i<=m;++i){
int x = find(g1[i].x),y = find(g1[i].y);
if(x == y)continue;
f[x] = f[y];
add(g1[i].x,g1[i].y,g1[i].dis);
add(g1[i].y,g1[i].x,g1[i].dis);//建树
}
}