最小生成树-Prim算法与Kruscal算法

以下文章:
\(N\)代表点数,\(M\)代表边数。
Prim算法
1.找到不在最小生成树中的距离最近的点t
2.将点t加入到集合中
3.利用点t更新其他不在最小生成树中的点。

const int N = 550;
int dist[N];
bool st[N];
int g[N][N];

int prim(){
  memset(dist , INF ,sizeof(dist));
  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;    
  }
  st[t] = true;
  if(i && dist[t] == INF) return INF;
  if(i) res += dist[t];
  for(int j = 1;j <= n;j++){
    dist[j] = fmin(dist[j] , g[t][j];
  }
  }
return res;
}

Kruscal算法
1.将所有边按照权重大小排序 $ O(M*logM) $
2.枚举每条边a,b权重c。
3.如果当前a,b不连通,则加入最小生成树的集合。
难点:如何快速得知两个点是否属于同一集合。 :并查集。

int find(int x){ //查询点x所在的集合
  if(p[x] != x) p[x] = find(p[x]);
  return p[x];
}
void union(int a,int b){ //将a点所属的集合与b点所属的集合合并。
  int x = find(a);
  int y = find(b);
  p[x] = y;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n,m;
int p[N];
struct Edge{
    int a,b,w;
}edges[N];
int cmp(Edge a, Edge b){
    return a.w < b.w;
}

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,w;
        scanf("%d%d%d",&a,&b,&w);
        edges[i] = {a,b,w};
    }
    sort(edges , edges + m, cmp); //按照权重从小到大排序
    
    for(int i = 1;i <= n;i++) p[i] = i;
    int res = 0,cnt = 0;
    
    for(int i = 0;i < m;i++){
        int a = edges[i].a,b = edges[i].b,w = edges[i].w;
        a = find(a); b = find(b);
        if(a != b){
            p[a] = b;
            res += w;
            cnt++;
        }
    }
    
    if(cnt < n - 1) puts("impossible"); // 存在负环
    else printf("%d\n",res);
    
    return 0;
}
posted @ 2022-10-18 16:43  Appreciate_小白  阅读(12)  评论(0)    收藏  举报