zoukankan      html  css  js  c++  java
  • 最小生成树

    最小生成树(MST)

    最小生成树(Minimum Spanning Tree)指在一个无向连通图中选出一些边,使得选出的这些边和原来的所有点构成的图依然连通且选出的边权之和最小,即边权和最小的生成树。

    最小生成树一般用于求解选择某条边对答案的贡献高于选择所有权值小于这条边的边对答案的贡献的问题,如在图中选出一条路径使得路径上权值最大的边的权值最小。

    最小生成树算法

    最小生成树算法用于求出一个无向图的最小生成树(森林)。

    Kruskal 算法

    Kruskal算法是一种常见且代码简单的算法,该算法主要思想是贪心,由Kruskal发明。

    时间复杂度:(O(mlog_2m))

    思路

    将所有的边从小到大排序,每次选择边权最小且加入后不会形成环的边加入最小生成树中,直到加完n-1条边(n表示点的数量)或者不存在可以继续加入的边。

    算法的正确性证明:

    假设存在一种选择使得这种选择比求出的最小生成树更优,那么按照边权排序,至少存在一组排名相同且最小的边a(求出的边)和b满足a,b不相同且(V_a>V_b)(V)表示权值)。

    在选择a之前,我们一定会考虑选择b,且考虑这两条边时已经选出来的边是相同的(因为两种方案中选择权值比这两条边小的边是相同的),即连通块是相同的。

    由于算法没有选择b,所以在这种情况下选择b会使生成树存在环,与假设不符,故不存在一种选择比求出的最小生成树更优。

    实现

    考虑连接一条边(x,y),当且仅当在连接这条边之前x,y已经连通,所以我们需要维护连通块,支持查询两点是否在同一连通块中、合并两个连通块,这个可以使用并查集(由于还没写,暂时链接到OI-wiki)实现。

    将边按照边权排序
    从小到大枚举所有边
    	如果当前边连接的两个点不在同一连通块中
    		在最小生成树中加入这条边
    		合并两个点所在的连通块
    

    Prim 算法

    Prim算法也是一种常用算法,主要思想是从一个点开始,不断加入点和对应的边最后形成一棵树,通常不用于求最小生成森林,更适用于稠密图(堆优化前),有时Prim算法也能够解决一些特殊的问题。

    时间复杂度:(O(n_2+m)) 或者 (O(mlog_2m)) (堆优化)

    思路

    先向最小生成树中加入一个节点,然后从枚举所有这个节点的出边,更新这些点到当前选出的点构成的连通块的最小距离,在未被选中的点中选出距离当前连通块最近的点加入连通块,更新其它点距离最小生成树的距离,重复此操作直到所有点都被加入连通块。

    算法正确性证明:

    假设有一种选法比算法选取的更优,那么一定有至少一条边的选择不同,找出其中一条边b,把这条边断开后分成的两个连通块拿出来,找到Prim算法中连接这两个连通块的边a,一定存在一条边b使得 (V_a>V_b)

    假装Prim得到了上面两个连通块中的一个(实际可能不会出现这种情况,但由于边时双向的,我们可以假装存在这种情况,这个对证明没有影响),然后算法选择了a连向的点而没有选择b连向的点,说明 (V_a) 小于等于其它所有从这个连通块连向其它点的边,与 (V_a>V_b) 冲突,所以不存在有一种选法比算法选取的更优。

    实现

    对每个点记录这个点是否已经被选和已经选择的点到这个点的最短距离,选择没被选的点中距离最短的点,更新其它点的最短距离,重复操作即可。

    任选一个即将被选的点
    循环 n 次
    	将即将被选的点标记为已经被选
    	枚举出边
    		如果到达的点的最小距离小于这条边的长度
    			将到达点的最小距离设为这条边的长度
    	枚举所有没被选的点,将距离最小的点设为即将被选的点
    

    堆优化

    每次选完一个点后找枚举所有没被选的点中距离最短的点时,视写法可能会枚举到已经被选的点或到达不了的点,考虑用一个数据结构维护可以到达的点中最近的点。

    一般来说,我们使用堆来优化,每次修改点的时候,我们将被修改的点和修改后的权值放进堆中(按权值从小到大的堆),每次选点的时候,先不断将堆顶所有已经被选的点弹出堆,然后选择堆顶的点进行操作即可。

    Boruvka 算法

    这种算法在实际运用中并不常见,但在某些特性场合可能会用到。

    思路

    考虑当前有一些连通块,对每个连通块求出距离这个连通块最近的连通块(距离定义为连接两个连通块的边的最小边权),将所有连通块与距离它最近的连通块合并。

    这个算法可以看成是Kruskal算法和Prim算法的结合,证明可以参考上面两种算法。

    实现

    每次枚举所有的边(或者其它方法),求出从每个连通块出去的最小边,将所有的最小边加入最小生成树,直到不存在连接不同连通块的边。

    时间复杂度

    我们每次操作一遍只枚举了所有边和所有点,复杂度是 (O(n+m)) 的,考虑最多要操作多少遍。

    每存在一条最小边连通块的数量就会减少1,一次操作中一条最小边最多在两个端点各被记录一次,所以最小边的数量的两倍一定大于等于连通块的数量,所以连通块的数量每次至少减少一半,所以最终的复杂度是 (O((n+m)log_2n)) 的。

    最小生成树的性质


    一个无向图的所有最小生成树的边权排序后都是相同的。

    证明同Prim算法。


    Kruskal算法中,处理完边权为某一个值的所有边后(可能有多种可行的方案),得到的连通块是相同的。

    证明:如果存在两种方案它们的连通块不同,那么其中至少有一种方案还能至少还能加一条边权为这个值的边。


    最小生成树上两点间的简单路径一定是原图中这两个点间路径中所经过最大边权值最小的路径之一。

    可以通过最小生成树的求法简单证明。


    akakw1
  • 相关阅读:
    高级树,AVL树和红黑树
    递归分治和动态规划
    树的便利
    undefined: balancer.PickOptions 报错
    微服务常用中间件
    golang中间件的实现
    OpenTracing与Jaeger
    selenium中driver.close()与driver.quit()的区别
    20201016_苹果开发者证书申请流程
    20201016_苹果开发者证书申请
  • 原文地址:https://www.cnblogs.com/akakw1/p/11862685.html
Copyright © 2011-2022 走看看