python--蓝桥杯--最小生成树

本文介绍了最小生成树(MST)的概念,探讨了MST的三个性质,重点讲解了Prim算法和Kruskal算法这两种常用的求解MST的贪心算法,以及它们在无向图中的应用。
摘要由CSDN通过智能技术生成

        最小生成树(Minimum Spanning Tree MST)是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权和最小。

        最小生成树的3个性质:

        1、最小生成树是树,因此其边数等于顶点数减1,且树内一定不会有环。

        2、对给定的图G(V,E),其最小生成树可以不唯一,但其边权和一定是唯一的。

        3、由于最小生成树是在无向图上生成的,因此其根节点可以是这棵树上的任意一个结点。

        求解最小生成树一般有两种算法,即prim算法和kruskal算法。这两个算法都采用了贪心的思想,只是贪心的策略不太一样。

        prim算法

        prim算法和Dijkstra算法十分相似,主要区别是d[]数组的含义不同。在Dijkstra算法中d[v]数组是结点v到达源点s的最短距离,在prim算法中d[v]数组是结点v到达集合S的最短距离。同样有邻接矩阵和邻接表版本。

如果有同学忘记了Dijkstra算法,可以看博主的上上篇关于最短路径的文章,链接参考如下👇

python--蓝桥杯--最短路径-CSDN博客

## 输入样例
6 10 0
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
def prim(s):
    global d,vis,ans
    d[0]=0
    for i in range(n):
        u=-1
        MIN=float("inf")
        for j in range(n):
            if vis[j]==False and d[j]<MIN:
                u=j
                MIN=d[j]
        if u==-1:
            return
        vis[u]=True
        ans+=d[u]
        for v in range(n):
            if vis[v]==False and G[u][v]!=float("inf"):
                if G[u][v]<d[v]:
                    d[v]=G[u][v]
    return ans
n,m,s=map(int,input().strip().split())
d=[float("inf")]*n
vis=[False]*n
ans=0
G=[[float("inf")]*n for _ in range(n)]
for _ in range(m):
    u,v,w=map(int,input().strip().split())
    ## 无向图
    G[u][v]=G[v][u]=w
print(prim(s))
## 输出样例
15

        kruskal算法同样是解决最小生成树问题的一个算法。和prim算法不同,kruskal算法采用了边贪心的策略,其思想极其简洁,理解难度比prim算法要低很多。

        kruskal算法的基本思想为:在初始时隐去图中的所有边,这样图中每个顶点都自成一个联通块。然后执行以下操作:

        1、对所有边按边权从小到大进行排序

        2、按边权从小到大测试所有边,如果当前测试边所连接的顶点不在一个联通块,则把这条测试边加入当前最小生成树中,否则舍弃。

        3、执行步骤2,直至最小生成树中的边数等于顶点数减1或是测试完所有边时结束。而当结束时如果最小生成树的边数小于顶点数减1,说明该图不连通。

## 输入样例
6 10 0
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
class edge:
    def __init__(self,u=None,v=None,w=None):
        self.u=u
        self.v=v
        self.w=w
## 并查集查询函数
def findFather(x):
#    a=x
    while (x!=father[x]):
        x=father[x]
#    while (a!=father[a]):
#       z=a
#        a=father[a]
#       father[z]=x
    return x

def kruskal(n,m):
    global father
    ans=0
    edge_num=0
    ## 顶点范围0-n-1
    for i in range(n):
        ## 并查集初始化
        father[i]=i
    ## 所有边按边权从小到大排列
    E.sort(key=lambda x:x.w)
    ## 枚举所有边
    for i in range(m):
        faU=findFather(E[i].u)
        faV=findFather(E[i].v)
        ## 如果不在一个集合中
        if faU!=faV:
            ## 合并集合
            father[faU]=faV
            ans+=E[i].w
            edge_num+=1
            if edge_num==n-1:
                break
    if edge_num!=n-1:
        return -1
    else:
        return ans
n,m=map(int,input().strip().split())
E=[[] for _ in range(m)]
for i in range(m):
    E[i]=edge()
    u,v,w=map(int,input().strip().split())
    E[i].u=u
    E[i].v=v
    E[i].w=w
father=[-1]*n
print(kruskal(n,m))
## 输出样例
15
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值