定义:
在一个有n个顶点的无向连通图中选择n-1条边以连通所有顶点,且选择的这n-1条边的权值总和最小,这样生成的子图称为这个图的最小生成树,而其它边权值总和大于它的子图为这个图的生成树。
特点:
有n-1条边,所有点都是连通的,但删掉任意一条边后就会有点不连通,且任意两点之间只有唯一一条简单路径(但在原图中不见是最短路径),而且这个最小生成树中不会存在环。
唯一性:
如果一个图中所有边的边权值都不相同,则该图一定有一个唯一的最小生成树,否则不一定唯一。
求解最小生成树总边权值和的方法:
一、Prim算法(O(n^2))
以一种贪心的思想,而遍历的方法类似广搜,即以一个顶点为起点每次得到最小距离的点并加入最小生成树,直到所有点都包含在内为止。
基本代码:(a[][]表示求解的图,dis[]表示最小生成树中的点到树中先前进入树的点最小距离,vis标记是否在树中)
准备工作,初始化dis[]中除了dis[1]为0外其它都为无穷大。
for(int i=1;i<=1;i++)
{
minn=1e18;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&dis[j]<minn)
aans=j;
}
vis[aans]=1;
ans+=dis[aans];
for(int i=1;i<=n;i++)
if(!vis[j]&&dis[j]>a[aans][j])
dis[j]=a[aans][j];
}
结果即最小生成树的总边权值即为ans;
二、Kruskal算法(O(m*(logm)))
也是一种贪心的思想,但还用了并查集的思想,通过每次选取两端点不同时在子树中的且权值最小的边来实现,直到选够n-1条构成总的最小生成树为止。如果遍历完所有边后不到n-1条边,说明不存在最小生成树。
基本代码:(a[]代表求解的图,f[]用以实现并查集)
int getfather(int x)
{
if(f[x]==x) return x;
f[x]=getfather(f[x]);
return f[x];
}
ll f1,f2;
ans=num=flag=0;
sort(a+1,a+m+1);
for(int i=1;i<=m;i++)
f[i]=i;
for(int i=1;i<=m;i++)
{
f1=getfather(a[i].l);
f2=getfather(a[i].r);
if(f1!=f2)
{
ans+=a[i].len;
f[f1]=f2;
num++;
if(num==n-1) break;
}
}
if(num!=n-1) flag=1;
结果即为ans,flag代表是否不存在最小生成树。