最小生成树MST(Prim/Kruskal模版)

生成树:如果连通图G的一个子图是一棵包含G的所有顶点的树,则该子图称为G的生成树。生成树不唯一,不同起点遍历所得生成树不同。
最小生成树:边权最小的生成树。

最小边原则:图中权值最小的边(如果唯一的话)一定在最小生成树上。

唯一性:一棵生成树上,如果各边的权都不相同,则最小生成树是唯一的。反之不然。

生成树一定是无向图。

Prim

基础是两个属性:
环属性:一棵生成树上,增加一条边e,再删除e所在环上的最大边,会得到另一棵“更好”的生成树(如果e不是最大边)
剪切属性:在图中,剪切将顶点划分成两个不相交集合。交叉边为这些顶点在两个不同集合的边。对于任何一个剪切,各条最小的交叉边都属于某个MST,且每个MST中都包含一条最小交叉边。

简述:MST_Prim(G, r)
(1)将G剪切成两个集合A、B,A中只有一个点r
(2)取最小权的交叉边(x,y),x∈A, y∈B
(3)将y加入A并更新
(4)如果已经加了n-1条边,结束。否则,转 (3)

示例
这里写图片描述

模版

int flag[maxm]={};//是否在A中。
int dis[maxm];//集合A与i点的交叉边权值,如果flag[i]==1,那么dis[i]无意义。
int sum=0;//MST边权总和。

void prim()
{
    flag[1]=1;//从任意一点出发均可,此处用第一个点。第一个点已被选入集合A。
    for(int i=1;i<=n;i++) dis[i]=a[1][i];//初始化。
    dis[1]=0; //已在A中,无交叉边。
    for(int i=1;i<n;i++)
    {
        int minn=1000000;
        int k=0;
        for(int j=1;j<=n;j++)
            if(!flag[j]&&dis[j]<minn) minn=dis[j],k=j;//在现有的交叉边中找到最小的。
        flag[k]=1;//将其加入A。
        sum+=dis[k];
        if(k==0) break;//同dijkstra,非联通图的情况。
        for(int j=1;j<=n;j++)
            if(!flag[j]&&dis[j]>a[k][j]) dis[j]=a[k][j];//利用新加入的点更新集合A与B的交叉边。
    }
    return; 
}

基于点的迭代,稳定O(n^2)。
堆优化后:O((|E|+|V|)*log|V|) ,适合稀疏图。



Kruskal

简述:MST_Kruskal(G)
(1)将G所有条边按权从小到大排序;图mst开始为空
(2)从小到大次序取边(x,y)
(3)若加入边(x,y),mst就有环,则放弃此边,转(2)
(4)将边(x,y)加入mst,如果已经加了n-1条边,结束。否则,转 (2)

判环即利用并查集,改日另写。

int n,m,summ=0;
struct edge
{
    int q,w,v;
}e[maxm];//边表。
int fa[maxn]={};//根节点。

sort(e+1,e+m+1,mycmp);//升序排序。

for(int i=1;i<=n;++i) fa[i]=i;//根节点初始化。

inline int find(int x)
{
    if(fa[x]==x) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}//找爸爸。

/*或者另一种装13的找爸爸写法:
inline int find(int x)
{
    return(fa[x]==x?x:fa[x]=find(fa[x]));
}*/

void kru()
{
    int flag=0;//边数计数器。
    for(int i=1;i<=m;i++)
    {
        int x=find(e[i].q);
        int y=find(e[i].w);
        if(x!=y)//如果它们不是同一个爸爸生的,即加入它们不会形成一个环。
        {
            fa[x]=y;//合并。
            flag++;//计数。
            summ+=e[i].v;
            if(flag==n-1) return;//如果边已达到n-1条,结束。
        }
    }
    return;
}

基于边的迭代,适合稀疏图,O(|E|*log|E| +|N|*A(|V|))。O(|E|*log|E| )用于排序。

最大生成树可把数据全部取负再操作。注意初始化问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值