返回

最小生成树(MST)算法学习

后端

最小生成树(MST)

最小生成树(Minimum Spanning Tree,MST)是指给定一个无向图,在所有可能的生成树中,权值和最小的生成树。

最小生成树在许多领域都有应用,例如网络通信、电路设计和物流配送等。

最小生成树算法

求解最小生成树的算法有很多,其中最经典的是克鲁斯卡尔算法和普里姆算法。

克鲁斯卡尔算法

克鲁斯卡尔算法是一种贪心算法,它从权值最小的边开始,逐步将边添加到生成树中,直到生成树包含所有顶点。

克鲁斯卡尔算法的具体步骤如下:

  1. 将图中的所有边按照权值从小到大排序。
  2. 从权值最小的边开始,将其添加到生成树中。
  3. 如果添加的边与生成树中已有的边形成环,则跳过该边。
  4. 重复步骤 2 和 3,直到生成树包含所有顶点。

普里姆算法

普里姆算法也是一种贪心算法,它从一个顶点出发,逐步将相邻的边添加到生成树中,直到生成树包含所有顶点。

普里姆算法的具体步骤如下:

  1. 选择一个顶点作为起点,将其添加到生成树中。
  2. 从生成树中选择一个顶点,找到与该顶点相邻且权值最小的边,将其添加到生成树中。
  3. 如果添加的边与生成树中已有的边形成环,则跳过该边。
  4. 重复步骤 2 和 3,直到生成树包含所有顶点。

洛谷 P3366 题目解析

洛谷 P3366 题目给定一个无向图,要求求出它的最小生成树。

我们可以使用克鲁斯卡尔算法或普里姆算法来求解该题目。

以下是使用克鲁斯卡尔算法求解该题目的代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Edge {
  int u, v, w;
};

bool cmp(const Edge& a, const Edge& b) {
  return a.w < b.w;
}

class DisjointSet {
public:
  DisjointSet(int n) {
    for (int i = 0; i < n; i++) {
      parent[i] = i;
      rank[i] = 0;
    }
  }

  int find(int x) {
    if (parent[x] != x) {
      parent[x] = find(parent[x]);
    }
    return parent[x];
  }

  void union(int x, int y) {
    int px = find(x);
    int py = find(y);
    if (px == py) {
      return;
    }
    if (rank[px] < rank[py]) {
      parent[px] = py;
    } else if (rank[px] > rank[py]) {
      parent[py] = px;
    } else {
      parent[py] = px;
      rank[px]++;
    }
  }

private:
  vector<int> parent;
  vector<int> rank;
};

int main() {
  int n, m;
  cin >> n >> m;
  vector<Edge> edges;
  for (int i = 0; i < m; i++) {
    int u, v, w;
    cin >> u >> v >> w;
    edges.push_back({u, v, w});
  }
  sort(edges.begin(), edges.end(), cmp);
  DisjointSet ds(n);
  int mst = 0;
  for (auto& edge : edges) {
    int px = ds.find(edge.u);
    int py = ds.find(edge.v);
    if (px != py) {
      ds.union(px, py);
      mst += edge.w;
    }
  }
  cout << mst << endl;
  return 0;
}

总结

本文详细介绍了最小生成树的概念、两种经典的求解算法(克鲁斯卡尔算法和普里姆算法)以及洛谷 P3366 题目的详细解析,非常适合对图论和算法感兴趣的朋友学习。