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

    什么是最小生成树呢?

    在此先说一下什么是树。

    树是图的一种,是无向无环联通图。意思是:首先,树是无向图,其次,不会形成环,且是连通图(从一个点可以到达其他所有的点)

    举个栗子

    这就是一颗树

    这样就不是了,因为形成了环 。

    树分为有根树无根树

    有根树:明确两个点的父子关系(可以认为所有边是有向的,由父亲指向儿子或相反)

    无根树:没有明确的父子关系,指定任意一个点当做跟都可以,但在指定跟以后,就变成了有根树。

    生成树:若图中有n个点(这张图里有6个),从图中选出n-1条边(这里就是选5条)构成一个树,即称为该图的生成树。

    最小生成树:生成树中边权之和最小的生成树。

    算法一:

    prim

      prim和dijkstra算法的思想比较像,每次添加没有添加过的,到当前的生成树距离最小的边(用d[i]记录点i到生成树的距离)。在这里默认①为根节点。将当前边权之和(ans)加上该边的权值,同时标记这条边的终点i(这里的“终点”是以生成树(把它视作一大坨点)为起点,边的终点)已经加入进生成树。一直找到最后,如果刚好找到n-1条边,那最终答案就是ans。(有的数据会让你找不到合适的可以加入的边)

      prim是以点为单位加入而不是以边为单位,所以用邻接矩阵存图即可(用前向星会TLE),适用于点少边多的数据。

    //代码from wz
    #include <bits/stdc++.h> using namespace std; int n, m; int t[5001][5001]; int d[5001]; bool vis[5001]; int ans, cnt;//ans为边权之和,cnt为当前的边数 int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) t[i][j] = 1000000005;//初始化为无限大 t[i][i] = 0; } for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; t[v][u] = t[u][v] = min(t[u][v], w);//对于不能直达的两个点来说,它们当前的距离是无限大,
    //并且若数据不断更新这条边,总能保证这个边的边权保持最小(数据可能会出现重边)(模板题血与泪的教训)
    } for (int i = 1; i <= n; i++) { d[i] = 1000000005;//初始化 } for (int i = 1; i <= n; i++) { d[i] = t[1][i];//记录最开始到当前生成树(只有根节点 ①的树)的距离 } d[1] = 0; vis[1] = 1; while (cnt <= n - 1) { int min_i = 0, min_d = 1000000005;//min_d寻找当前可以加入的边权最小的边 for (int i = 1; i <= n; i++) { if (!vis[i] && d[i] < min_d)//判断是否合法 { min_d = d[i]; min_i = i;//min_i记录这条边的终点 } } if (min_i == 0)//如果找不到就跳出循环 { break; } cnt++;//已经加入的边数加一 ans += min_d; d[min_i] = 0;//i已经加入进生成树,所以i和生成树的距离是0 vis[min_i] = 1;//标记i已经加入了 for (int i = 1; i <= n; i++) { d[i] = min(d[i], t[min_i][i]); } } if (cnt == n - 1) { cout << ans << endl; } else { cout << -1 << endl; } }

    算法二:

    kruskal 

     本质和prim差不多,都是加入当前的最小边。kruskal算法先将所有边从小到大排序,看加入每条边后是否成环,如果成环就不取,不成环就取。

     思想好理解,重点在于如何判环。

     这里我们要用到并查集。将所有已经加入的边的端点放入同一个集合中,再加入边时,只需要判断这条边的起点和终点是否在同一集合里(如果在,加入后就会形成环)。

    //代码from最最美丽的wz小姐姐
    #include<bits/stdc++.h>
    using namespace std;
    struct edge{
        int from,to,dis;
    }g[500001];
    bool cmp(edge a,edge b){
        return a.dis<b.dis;
    }
    int fa[200001];
    int getf(int x){//并查集的并 
        if(fa[x]==x)return x;
        fa[x]=getf(fa[x]);
        return fa[x];
    }
    int n,m,cnt;
    long long ans;
    int main(){
        cin>>n>>m;
        for(int i=1;i<=m;i++){
            cin>>g[i].from>>g[i].to>>g[i].dis;
        }
        for(int i=1;i<=n;i++){
            fa[i]=i;//并查集初始化 
        }
        sort(g+1,g+m+1,cmp);//将边排序 
        for(int i=1;i<=m&&cnt<=n-1;i++){
            int fu=getf(g[i].from),fv=getf(g[i].to);
            if(fu==fv)continue;//如果要加入的边的起点和终点已经在生成树里了,就不加入 
            fa[fu]=fv;//加入后合并两点 
            cnt++;//总边数+1 
            ans+=g[i].dis;//答案加上边权 
        }
        if(cnt==n-1){
            cout<<ans<<endl;
        }else{
            cout<<"No Solution.
    ";
        }
    }
  • 相关阅读:
    COM组件
    【游戏引擎架构】入门(一)
    UNICODE字符串
    Python随笔10
    Python随笔9-函数
    Python随笔7
    Python随笔6
    Python随笔5
    Python随笔4
    Python随笔3
  • 原文地址:https://www.cnblogs.com/lcez56jsy/p/10776291.html
Copyright © 2011-2022 走看看