• 最小生成树


    最小生成树可以说是图论入门的基础题型,现总结如下。

    什么是最小生成树?

    我们知道树是一种图,每个结点都有与之相连的许多结点,对于一个树,给定它的边,每个边都有各自的权值,我们希望以最小的代价来使树上个点相连,那么我们就需要去找出一种实现这样的期望的方法,即我们所要求的,就是最小生成树。

    常见算法:1、Prim算法:首先将点分为两类,已选择的和未选择的,先随机选一个点作为初始点每一步从所有连接两种类型的点的边中,选取一条权值最小的加入生成树,再将点加入已选择当中。复杂度取决于如何选择最小边,暴力选取复杂度为O(MN),二叉堆维护为O(MlogN),斐波那契堆维护为O(M+NlogN)。类似Dijkstra,我们需要一个dis数组和vis数组

         2、Kruskal算法:使用并查集维护树的连通性,首先将所有边按从小到大排序,并认为每一个点是孤立的,按从小到大的顺序枚举每一条边,如果这条边连接的两个点不在一个连通块内,那么就把这条边加入最小生成树,将两个连通块合并,反之continue复杂度O(mlogm+nk);

    二者区别:Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。Prim是以更新过的节点的连边找最小值,Kruskal是直接将边排序。

    Prim代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define il inline
    il int read()
    {
        re int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x*f;
    }//快读,不理解的同学用cin代替即可
    #define inf 123456789
    #define maxn 5005
    #define maxm 200005
    struct edge
    {
        int v,w,next;
    }e[maxm<<1];
    //注意是无向图,开两倍数组
    int head[maxn],dis[maxn],cnt,n,m,tot,now=1,ans;
    //已经加入最小生成树的的点到没有加入的点的最短距离,比如说1和2号节点已经加入了最小生成树,那么dis[3]就等于min(1->3,2->3)
    bool vis[maxn];
    //链式前向星加边
    il void add(int u,int v,int w)
    {
        e[++cnt].v=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
    }
    //读入数据
    il void init()
    {
        n=read(),m=read();
        for(re int i=1,u,v,w;i<=m;++i)
        {
            u=read(),v=read(),w=read();
            add(u,v,w),add(v,u,w);
        }
    }
    il int prim()
    {
        //先把dis数组附为极大值
        for(re int i=2;i<=n;++i)
        {
            dis[i]=inf;
        }
        //这里要注意重边,所以要用到min
        for(re int i=head[1];i;i=e[i].next)
        {
            dis[e[i].v]=min(dis[e[i].v],e[i].w);
        }
        while(++tot<n)//最小生成树边数等于点数-1
        {
            re int minn=inf;//把minn置为极大值
            vis[now]=1;//标记点已经走过
            //枚举每一个没有使用的点
            //找出最小值作为新边
            //注意这里不是枚举now点的所有连边,而是1~n
            for(re int i=1;i<=n;++i)
            {
                if(!vis[i]&&minn>dis[i])
                {
                    minn=dis[i];
                    now=i;
                }
            }
            ans+=minn;
            //枚举now的所有连边,更新dis数组
            for(re int i=head[now];i;i=e[i].next)
            {
                re int v=e[i].v;
                if(dis[v]>e[i].w&&!vis[v])
                {
                    dis[v]=e[i].w;
                }
            }
        }
        return ans;
    }
    int main()
    {
        init();
        printf("%d",prim());
        return 0;
    }

    Kruskal代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn = 1e5+5;
    struct node{
        int from,to,v;
        friend bool operator < (const node& a,const node& b) {
            return a.v<b.v;
        }
    }map[2*maxn];
    int n,m;
    int fa[5001];
    /*void add(int x,int y,int z) {
        count++;
        to[count] = y;
        v[count] = z;
        next[count] = head[x]
        head[x] = count;
    }*/
    int get(int x) {
        if(fa[x] == x) return x;
        else return fa[x] = get(fa[x]);
    }
    bool Union(int x,int y) {
        if(get(x) == get(y)) return true;
        else return false;
    }
    int main() {
        cin>>n>>m;
        int x,y,z,ans=0;
        for(int i=1; i<=n; i++) fa[i] = i;
        for(int i=1; i<=m; i++) {
            cin>>x>>y>>z;
            map[i] =(node){x,y,z}; 
        }
        int num=0;
        sort(map+1,map+1+m);
        for(int i=1; i<=m; i++) {
            if(num==n-1) {
                cout<<ans;
                return 0;
            }
            if(!Union(map[i].from,map[i].to)){
                fa[get(map[i].from)] = get(map[i].to);
                ans+=map[i].v;
                num++;    
            } 
        }
        cout<<"orz";
        return 0;
    } 

    当然还有其他实现最小生成树的办法,后面会补充

  • 相关阅读:
    2016年3月9日~10日,杨学明老师为武汉某著名通信企业提供内训课程服务!
    打造高效率产品测试体系--产品测试管理(深圳,2016.3.18~19)
    互联网产品上线前,做些什么——产品、开发、测试的视角
    2016年1月16日,《互联网项目管理高级实务》内训在上海某高科技企业成功举办!
    用C++对C++语法格式进行分析
    mysql主从配置
    Windows Zip/CentOS/Radhat系统安装Mysql5.7.x方法
    c++预声明类引发的无法解析外部符号问题
    解决VS2015单元测试“未能设置用于运行测试的执行上下文”问题
    扩展Linux磁盘空间
  • 原文地址:https://www.cnblogs.com/delta-cnc/p/12321251.html
Copyright © 2020-2023  润新知