f i r s t 最 小 生 成 树 first 最小生成树 first最小生成树
最小生成树指的是在一个边有权的树上,求一个边权和最小的树形结构,并且使得这个树形结构要覆盖这个边上的所有点。现在求最小生成树的算法主要有两种,一个是 p r i m e prime prime 算法,一个是 K r u s k a l Kruskal Kruskal 算法。
一 prime算法
1.定义
我们现在有一个集合 A A A和一个集合 E E E,分别为初始的点集合和初始的边集合,此外我们还有一个集合 B B B ,为记录我们选择的点的集合,集合 E a n s Eans Eans 是被选择边的集合,开始的时候任意从 A A A 中选择一个点加入到 B B B 中,随后查找距离 B B B 中点最近的点,将它加入到 B B B 中,将使用的边加入到集合 E a n s Eans Eans 中去。最终 E a n s Eans Eans 中的边就是我们的最小生成树的所有边。
2 代码实现
memset(minn,127,sizeof minn);
minn[1] = 0;
for(int i = 1;i <= n;i++)
{
int k = 0;
for(int j = 1;j <= n;j++) if(!vis[j]&&(minn[j] < minn[k])) k = j; // 得到距离集合点最近的点的位置
ans += min[k]; // 这条边就是我们的选择
vis[k] = true; // 标记已经被选择
for(int j = 1;j <= n;j++)
if(!vis[j]&&f[k][j])
{
minn[j] = min(minn[j],f[k][j]); // 基于这个点再进行更新一波,
}
}
3 prime算法的优化
在prime算法中,我们可以知道我们要去选择的是距离点集合 B B B 最近的那个点,对于这样一个问题,我们可以使用二叉堆进行优化,在小根堆中,最顶端的永远是最小的那个数,听大佬说再稠密图中使用堆优化的效果一般,emmm
二叉堆的实现 ,其实也可以直接使用 q u e u e . h queue.h queue.h中的 p r i o r i t y _ q u e u e priority\_queue priority_queue,就不用自己再去造轮子了
memset(dis,0x7f,sizeof(dis)); dis[1]=0;
cnt=0;
q.push(data{1,0});
while(!q.empty()&&cnt<n)
{
int d=q.top().cost,u=q.top().to;
q.pop();
if(vis[u]) continue;
++cnt; ans+=d; vis[u]=1;
for(int i=head[u];i;i=E[i].next)
if(E[i].cost<dis[E[i].to])
dis[E[i].to]=E[i].cost,q.push(data{E[i].to,E[i].cost});
}
if(cnt==n) printf("%d",ans);
else printf("-1");
二 Kruskal算法
1. 定义
kruskal算法核心其实就是并查集,不断合并块,直至最后只剩一个块,每次选择的是最短的边,如果两边不同就合并。
2 code
int k = 0;
sort(a+1,a+1+m);
for(int i = 1;i <= m;i++)
{
if(find(a[i].x)!=find(a[i].y))
{
ans = a[i].value;
join(a[i].x,a[i].y);
k++;
if(k==n-1) break;
}
}