最小生成树详解

一、概念

1、

最小生成树是一副连通加权无向图中一棵权值最小的生成树

如下图黑线表示的是一个最小生成树

2、

一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林

 

二、相关性质

1、个数多个

最小生成树在一些情况下可能会有多个

例如,当图的每一条边的权值都相同时,该图的所有生成树都是最小生成树。

例如下图

 

如果图的每一条边的权值都互不相同,那么最小生成树将只有一个,这一定理同样适用于最小生成森林。

2、边的权值之和最低的子图

如果图的边的权值都为正数,那么最小生成树就是该图的所有包含所有顶点的子图中权值最低的子图。

3、环定理

对于连通图中的任意一个环C:如果C中有边 e的权值大于该环中任意一个其它的边的权值,那么这个边不会是最小生成树中的边

证明:
假设 e属于最小生成树 {\displaystyle T1},那么将 e删去将会使得 {\displaystyle T1}变为两个树。因为环 C必然还存在另一横切边f可以连接两个子树形成生成树 {\displaystyle T2},且由于 f< e,生成树 {\displaystyle T2}权值更小,与 {\displaystyle T1}是最小生成树矛盾。

4、最小权值边

如果图的具有最小权值的边只有一条,那么这条边包含在任意一个最小生成树中。

 

三、Prim(普利姆算法)

1、思想

 1.从图中选取一个节点作为起始节点(也是树的根节点),标记为已达;初始化所有未达节点到树的距离为到根节点的距离;

 2. 从剩余未达节点中选取到树距离最短的节点i,标记为已达;更新未达节点到树的距离(如果节点到节点i的距离小于现距离,则更新);

 3. 重复步骤2直到所有n个节点均为已达。

 

图例说明不可选可选已选
Prim Algorithm 0.svg此为原始的加权连通图。每条边一侧的数字代表其权值。---
Prim Algorithm 1.svg顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。C, GA, B, E, FD
Prim Algorithm 2.svg下一个顶点为距离DA最近的顶点。BD为9,距A为7,ED为15,FD为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。C, GB, E, FA, D
Prim Algorithm 3.svg算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。CB, E, GA, D, F
Prim Algorithm 4.svg在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。E最近,因此将顶点E与相应边BE高亮表示。C, E, GA, D, F, B
Prim Algorithm 5.svg这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。C, GA, D, F, B, E
Prim Algorithm 6.svg顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EGGA, D, F, B, E, C
Prim Algorithm 7.svg现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。

A, D, F, B, E, C, G

 

2、CODE:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
typedef long long LL;
using namespace std;
#define memset(a,n) memset(a,n,sizeof(a))
#define INF 0x3f3f3f3f

int dis[105];
int vis[105];
int mapp[105][105];
int n,m;

void Prim()
{
    int sum=0;
    for(int i=1;i<=m;i++)
        dis[i]=mapp[1][i];

    vis[1]=1;
    dis[1]=0;

    int minn=INF,pos=INF;
    for(int i=1;i<m;i++){ // 只进行顶点-1 次

        minn=INF;
        pos=INF;

        for(int j=1;j<=m;j++){
            if(vis[j]==0&&dis[j]<minn){ // 找一个最小的边
                minn=dis[j];
                pos=j;
            }
        }

        vis[pos]=1;
        sum+=minn;
     
     // 更新两点间的距离
        for(int j=1;j<=m;j++)
            if(vis[j]==0&&dis[j]>mapp[pos][j])
                dis[j]=mapp[pos][j];

    }
  
  printf("%d\n",sum);
}

int main()
{
    int be,en,v;
    while(scanf("%d %d",&n,&m)&&n!=0) // m 为顶点,n 为边
    {
        memset(vis,0);
        memset(dis,INF);
        memset(mapp,INF);

        for(int i=0;i<n;i++){
            scanf("%d %d %d",&be,&en,&v);
            if(mapp[be][en]>v) // 防止重边,只保留这两个顶点间更小的一个边
                mapp[be][en]=mapp[en][be]=v;
        }

        Prim();
    }
}

 

四、Kurskal(克鲁斯卡尔算法)

1、思想

 

  1. 新建图G,G中拥有原图中相同的节点,但没有边
  2. 将原图中所有的边按权值从小到大排序
  3. 从权值最小的边开始,这条边与图中已有的边不形成环,则加入该条边
  4. 重复3,直至图G中所有的节点都在同一个连通分量中

2、判断成不成环,需要用到 并查集

3、CODE:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
typedef long long LL;
using namespace std;
#define memset(a,n) memset(a,n,sizeof(a))
#define INF 0x3f3f3f3f

int pre[110];
int find(int x)
{
    if(x!=pre[x]){
        pre[x]=find(pre[x]);
    }
    return pre[x];
}
void Merge(int a,int b)
{
    int root1=find(a);
    int root2=find(b);
    if(root1!=root2){
        pre[root1]=root2;
    }
}

int main()
{
    int n,m,a,b;
    while(scanf("%d",&n)&&n!=0){
        scanf("%d",&m);

        for(int i=1;i<=n;i++)
            pre[i]=i;

        for(int i=0;i<m;i++){
            scanf("%d %d",&a,&b);
            Merge(a,b);
        }

        int sum=0;
        for(int i=1;i<=n;i++){
            if(pre[i]==i)
                sum++;
        }
        sum--;

        printf("%d\n",sum);
    }
}

二、与最短路区别:

最小生成树能够保证整个拓扑图的所有路径之和最小,但不能保证任意两点之间是最短路径。
最短路径是从一点出发,到达目的地的路径最小。

 

三、例题详解

有关例题

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值