最小生成树核心Algorithm(C++)

最小生成树核心Algorithm

问题描述:给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。

由 V 中的全部 n 个顶点和 E中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式

第一行包含两个整数 n 和 m。

接下来 m 行,每行包含三个整数 u,v,w表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

1≤n≤10^5, 1≤m≤2∗10^5, 图中涉及边的边权的绝对值均不超过 1000。

输入样例:

 4 5
 1 2 1
 1 3 2
 1 4 3
 2 3 2
 3 4 4

输出样例:

 6

Prim Algorithm

code:

 #include<cstdio>
 #include<cstring>
 #include<iostream>
 #include<algorithm>
 using namespace std;
 
 const int N = 510,INF = 0x3f3f3f3f;
 int g[N][N],dist[N];
 bool st[N];
 int n,m;
 
 int prim(){
     //初始化点到生成树集合的距离为∞
     memset(dist,0x3f,sizeof dist);
     
     int res = 0;
     //迭代n次后n个点就全部加入到最小生成树中了
     for(int i=0;i<n;i++){
         int t = -1;
         //每次找到到集合距离最短的点,
         for(int j=1;j<=n;j++){
             if(!st[j]&&(t==-1||dist[t]>dist[j]))
                 t = j;
        }
         //如果i不是第一个点,且所有点中dist最小为INF,说明该图不连通,就说明最小生成树不存在,就返回INF
         if(i&&dist[t]==INF) return INF;
         
         //先加入、再更新!!!!!!!!,否则如果存在负权自环,那么他会把它自身先更新了,会影响结果。
         if(i) res += dist[t];
         for(int j=1;j<=n;j++) dist[j] = min(dist[j],g[t][j]);
         
         
         st[t] = true;
    }
     
     return res;
 }
 int main(){
     scanf("%d%d",&n,&m);
     memset(g,0x3f,sizeof g);
     while(m--){
         int a,b,c;
         scanf("%d%d%d",&a,&b,&c);
         g[a][b] = g[b][a] = min(g[a][b],c);
    }
     
     int t = prim();
     if(t==INF) puts("impossible");
     else printf("%d\n",t);
     return 0;
 }

Kruskal Algorithm

code:

 #include<cstdio>
 #include<cstring>
 #include<iostream>
 #include<algorithm>
 using namespace std;
 
 const int N = 2e5 + 10;
 int p[N];
 struct Edge{
     int a,b,w;
     
     //重载<运算符,方便下文中排序
     bool operator<(const Edge &e)const {
         return w<e.w;
    }
 }edge[N];
 
 int find(int x){//并查集模板
     if(x != p[x]) p[x] = find(p[x]);
     return p[x];
 }
 
 int main(){
     int n,m;
     scanf("%d%d",&n,&m);
     for(int i=0;i<m;i++){
         int a,b,c;
         scanf("%d%d%d",&a,&b,&c);
         edge[i] = {a,b,c};
    }
     
     sort(edge,edge+m);
     for(int i=1;i<=n;i++) p[i] = i;//并查集初始化
     int res = 0;//最小生成树的权值和,初始化为0
     int cnt = 0;//已经在生成树中的边,要想成为最小生成树,最基本的条件是当cnt==边数-1
     for(int i=0;i<m;i++){
         int a = edge[i].a,b = edge[i].b,w = edge[i].w;
         int fa = find(a),fb = find(b);
         if(fa != fb){//凡是两栖边 就将两点连入最小生成树
             res += w;
             cnt++;
             p[fa] = fb;
        }
    }
     
     if(cnt < n-1) puts("impossible");
     else printf("%d\n",res);
     return 0;
 }

 

posted @ 2021-05-26 22:07  how_you_make_me_feel  阅读(178)  评论(0编辑  收藏  举报