生成树
生成树,好长时间没有学习,居然快忘了什么是生成树了
给定一张边带权的无向图G,由n个顶点和n-1条边构成的无向联通子图叫做一个生成树,边的权值之和最小的生成树叫做最小生成树
也就是说如何选出来n-1条边来链接n个点直到连通
kruskal
既然说了是最小的,那么自然地想起来首先选择当前最小的边,然后直至选择了n-1条边为止,而且这两个端点属于两个不同的树(集合)也就是不联通,那把就把他们加一块,具体的步骤如下:
1.建立一个并查集,每一个点自己独立一个集合
2.把所有边按照权值从小到达的排序,以便我们选择最小的边,依次地扫描每一条边
3.如何这两个点连通(属于同一个集合),那么就跳过这一条边
4.不然,合并两个所在的集合,并且加上这个答案,所有边扫描完成之后,第4步处理过的边就是一个最小生成树
这个算法类似于贪心,用的是排序加上并查集
int get(int x)
{//获取元素代表
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>e[i].x>>e[i].y>>e[i].z;
sort(e+1,e+1+m);
for(int i=1;i<=n;i++)
{
fa[i]=i;//独立的集合
}
for(int i=1;i<=m;i++)
{
int x=get(e[i].x);
int y=get(e[i].y);
if(x==y) continue;
fa[x]=y;//合并
ans+=e[i].z;
}
}
Prim
Prim算法的思路稍稍有些改变,维护最小生成树的一部分,他只能确定1号节点属于的最小生成树,有点类似于求单源最短路经
可以类比dijkstra算法,每次从未标记的节点中选出的d值最小的标记一下然后扫描一下所有的出边,更新另一个点的值,主要用于稠密图,尤其是完全图
void prim()
{
memset(d,0x3f,sizeof(d));
memsmet(V,0,sizeof(v));
d[1]=0;
for(int i=1;i<n;i++)
{
int x=0;
for(int j=1;j<=n;j++)
if(v[j]==0&&(x==0||d[j]<d[x]))x=j;
v[x]=1;
for(int y=1;y<=n;y++)
if(v[y]==0) d[y]=min(d[y],a[x][y]);
}
}