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

    最小生成树

    大纲

    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.

  • 相关阅读:
    2014百度之星资格赛1001
    ZOJ1913 Euclid's Game (第一道简单的博弈题)
    博弈论(转)
    字典序全排列
    windows下用虚拟机安装ubuntu
    Windows多线程多任务设计初步(转)
    为什么我如此热爱这样一个比赛(转自vici)
    BFS/DFS算法介绍与实现(转)
    美丽的茧
    求N个数的最大公约数和最小公倍数(转)
  • 原文地址:https://www.cnblogs.com/xiaoyezi-wink/p/10822111.html
Copyright © 2011-2022 走看看