Kruskal算法

稀疏图中使用Kruskal算法进行。

思路

  1. 将所有边按权重从小到大排序(快排)( O ( m l o g m ) O(mlogm) O(mlogm)
  2. 枚举每条边a->b 权重为c,如果a和b不连通,就将这条边加到集合中,相当于是在a,b之间加一条边。 ( O ( m ) O(m) O(m)

题目

例1:Acwing 859

给定一个 n 个点 m条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。
给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合, n = ∣ V ∣ n=|V| n=V m = ∣ E ∣ m=|E| m=E
由 V 中的全部 n 个顶点和 E中 n−1 条边构成的无向连通子图被称为 G的一棵生成树,
其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式
第一行包含两个整数 n和 m。
接下来 m 行,每行包含三个整数 u,v,w,表示点 u和点 v之间存在一条权值为 w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围
1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105,
1 ≤ m ≤ 2 ∗ 1 0 5 1≤m≤2*10^5 1m2105,
图中涉及边的边权的绝对值均不超过 1000。

输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4

输出样例:
6

#include<bits/stdc++.h>
using namespace std;
// n表示的是顶点数目,m表示的是边数
int n,m;
const int N = 1e5+10;
const int M = 2e5+10;
// 并查集
int p[N];
struct Node{
    int from;
    int to;
    int val;
}edges[M];
bool cmp(const Node& a,const Node& b){
    return a.val < b.val;
}
int find(int x){
    if(p[x]!=x)
        p[x] = find(p[x]);
    else
        return p[x];
}
int main(){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    // 输入
    cin>>n>>m;
    for(int i = 0;i<m;i++){
        cin>>edges[i].from>>edges[i].to>>edges[i].val;
    }
    // 排序
    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].from ,  b = edges[i].to , val = edges[i].val;
        
        a = find(a),b = find(b);
        // 表示a,b之间之前没有连通的点的话
        if(a != b){
            p[a] = b;
            res += val;
            cnt++;
        }
    }
    if(cnt < n-1){
        cout<<"impossible"<<endl;
    }else{
        cout<<res<<endl;
    }
    return 0;
}
import java.util.Scanner;
import java.util.*;

class Main{
  private int n;
  private int m;
  private static final int N = 100010;
  // 声明父集合
  private static int[] p = new int[N];
  private static class Edge implements Comparable<Edge>{
    private int a;
    private int b;
    private int val;
    
    Edge(int a, int b, int val) {
        this.a = a;
        this.b = b;
        this.val = val;
    }
    int getA(){
      return this.a;
    }
    int getB(){
      return this.b;
    }
    int getVal(){
      return this.val;
    }
    @Override
    public int compareTo(Edge other){
      return Integer.compare(this.val,other.val);
    }
  }
  private static int find(int a){
    if(p[a] != a){
      p[a] = find(p[a]);
    }
      return p[a];
  }
  public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int m = sc.nextInt();
    

    Edge[] edges = new Edge[m];
    for(int i = 0;i<m;i++){
      int a = sc.nextInt();
      int b = sc.nextInt();
      int val = sc.nextInt();
      edges[i] = new Edge(a, b, val);
    }
    Arrays.sort(edges);

    // 初始化父集合
    for(int i = 1;i<=n;i++){
      p[i] = i;
    }
    int res = 0;
    int cnt = 0;
    for(int i = 0;i<m;i++){
      int a = edges[i].getA();
      int b = edges[i].getB();
      int val = edges[i].getVal();
      a = find(a);
      b = find(b);
      if(a != b){
        p[a] = b;
        res += val;
        cnt ++;
      }
    }
    if(cnt<n-1){
      System.out.println("impossible");
    }else{
      System.out.println(res);
    }
    sc.close();

  }
}
Kruskal算法是一种用来求加权连通图的最小生成树(MST)的算法,由Joseph Kruskal在1956年发表。该算法的基本思想是按照边的权值从小到大的顺序选择边,并保证这些边不构成回路。具体做法是首先构造一个只含有n个顶点的森林,然后依据权值从小到大从连通网中选择边加入到森林中,并保证森林中不产生回路,直至森林变成一棵树为止。 使用Kruskal算法求解最小生成树的过程主要有两个关键步骤。首先,需要对图的所有边按照权值大小进行排序。其次,需要判断在将边添加到最小生成树中时是否会形成回路。通过这两个步骤,Kruskal算法能够找到图的最小生成树。 总结来说,Kruskal算法通过按照边的权值从小到大选择边,并保证不形成回路的方式来构建最小生成树。这种算法适用于解决求解加权连通图的最小生成树问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Kruskal算法简易教程(附最全注释代码实现)](https://blog.csdn.net/hzf0701/article/details/107933639)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [克鲁斯卡尔算法Kruskal)详解](https://blog.csdn.net/weixin_45829957/article/details/108001882)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值