• 最小生成树


    最小生成树

    大纲

    1. Kruskal算法

    2. Prim算法 and/or Dijkstra算法

    最小生成树的概念

    图?

    由一个点集V(表示所有点)和一个边集E(表示所有边)共同构成的数据结构。V和E甚至可以是空集。

    延伸:

    1. 有向图、无向图

    2. 连通图、强连通图 连通图指所有点都相连;强连通图指在有向图中,每两个点都可以互相到达($ herefore强连通图subset连通图​$)

    3. 有向无环图(DAG)、无向连通图

    4. 重边(两个点间有两条或以上的直连边)、自环(自己指向自己的边)

    5. ...

    树?

    无向无环连通图。

    有根树,无根树

    有根树中明确指定了两个点间的父子关系,所以可以认为边是有向的(由父亲指向儿子或相反) 无根树没有明确的父子关系,指定任意一个点当做根都可以。注意当选定作为根的顶点后,父子关系就明确了,无根树就变成了有根树。

    生成树?

    设图中有n个顶点,选出n-1条边来构成一个树,即称为该图的生成树。

    最小生成树?

    在某个图的所有生成树里边权之和最小的那个。

    Kruskal算法

    算法流程

    初始化最小生成树中没有任何边。 然后由长度从小到大依次考虑每条边: 若加入最小生成树后形成了环,则不加入;否则加入。

    分析

    排序过程可以直接使用STL库的sort。 算法的瓶颈在于判断环上。

    解法

    使用并查集维护顶点之间的连接关系。 开始时没有任何点相连,即初始化并查集。 判断环时,只需判断该边的两个顶点是否原来就相连,即是否在一个集合中。 加边时,合并集合。

    证明(来自OI wiki)

    思路很简单,为了造出一棵最小生成树,我们从最小边权的边开始,按边权从小到大依次加入,如果某次加边产生了环,就扔掉这条边,直到加入了 $n-1$ 条边,即形成了一棵树。

    证明:使用归纳法,证明任何时候 K 算法选择的边集都被某棵 MST 所包含。

    基础:对于算法刚开始时,显然成立(最小生成树存在)。

    归纳:假设某时刻成立,当前边集为 $F$ ,令 $T$ 为这棵 MST,考虑下一条加入的边 $e$ 。

    如果 $e$ 属于 $T$ ,那么成立。

    否则, $T+e$ 一定存在一个环,考虑这个环上不属于 $F$ 的另一条边 $f$ (一定只有一条)。

    首先, $f$ 的权值一定不会比 $e$ 小,不然 $f$ 会在 $e$ 之前被选取。

    然后, $f$ 的权值一定不会比 $e$ 大,不然 $T+e-f$ 就是一棵比 $T$ 还优的生成树了。

    所以, $T+e-f$ 包含了 $F$ ,并且也是一棵最小生成树,归纳成立。

    【代码】:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    int n,m,k;
    long long ans;
    const int maxn=500005;
    int fa[maxn];
    
    struct edge   //起点,终点,权值 
    {
        int x,y,z;
    }a[maxn];
    
    int find(int x)   //通过是否具有同一个父亲(是否在同一集合里)判断是否连通成环 
    {
        if(fa[x]!=x) fa[x]=find(fa[x]);
        return fa[x];
    }
    
    bool cmp(edge x, edge y)  //排序 
    {
        return x.z<y.z;
    }
    
    void kruskal()     //核心算法 
    {
        for(int i=1;i<=n;i++) fa[i]=i;
            
        sort(a+1,a+m+1,cmp);   
        
        for(int i=1;i<=m&&k<=n-1;i++)
        {
            int f1=find(a[i].x);
            int f2=find(a[i].y);
            if(f1!=f2)
            {
                ans+=a[i].z;
                fa[f1]=f2;    //合并,连通起来
                k++;
            }
        }
        
        if(k==n-1)
          cout<<ans<<endl;
        
        else cout<<"No Solution.
    ";
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        
        for(int i=1;i<=m;i++)
          scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    
        kruskal();
        
        return 0;
        
    }

    相关题目:

    1.P3366 【模板】最小生成树

    2.U69308 【常数PK系列】 #3 最小生成树

    3.T78748 【lcez模拟赛】机场Ⅰ

      WZ的题面描述啊不行的话是输出No Solution.

  • 相关阅读:
    vc++ generate random via random_device and uniform_int_distribution
    Windows OpenGL 图像对比度调节
    OpenGL ES 版本介绍
    Windows OpenGL 图像伽马线
    Windows OpenGL 图像反色
    OpenGL ES 简介
    Windows OpenGL ES 图像饱和度调节
    修改 Docker 数据根目录
    mysql分库备份与分表备份
    docker dockerfile 映射端口范围 批量映射端口
  • 原文地址:https://www.cnblogs.com/xiaoyezi-wink/p/10822111.html
Copyright © 2020-2023  润新知