qidao123.com技术社区-IT企服评测·应用市场

标题: 最小生成树 & 严格次小生成树 [打印本页]

作者: 立山    时间: 2025-4-30 21:22
标题: 最小生成树 & 严格次小生成树
最小生成树

作甚最小生成树?

有一类题目:给定一张图,可以删除若干条边,在不改变连通性(一般是全联通)的情况下,权值和最小的方案是什么?没错,这就是最小生成树题目(MST题目)。那么根本性质其实连聪明的小学生都能看出来,应当使得末了留下 \(n-1\) 条边且没有环路得到情况下才有大概构成生成树,这便是Kruskal的根本实现原则,这个后面会讲。
最小生成树的Prim算法

其实Prim本身照旧比较好明白的,跟Dijstra没什么两样,方法如下:
Code:
  1. #include <iostream>
  2. #include <vector>
  3. #include <climits>
  4. using namespace std;
  5. const int MAXN = 5005;
  6. const int INF = INT_MAX;
  7. int n, m; // 顶点数和边数
  8. int graph[MAXN][MAXN]; // 邻接矩阵存储图
  9. int dist[MAXN]; // 存储顶点到MST的最小距离
  10. bool visited[MAXN]; // 标记顶点是否已在MST中
  11. void prim() {
  12.     // 初始化距离数组
  13.     fill(dist, dist + n + 1, INF);
  14.     fill(visited, visited + n + 1, false);
  15.    
  16.     dist[1] = 0; // 从顶点1开始
  17.    
  18.     int totalWeight = 0; // 最小生成树的总权重
  19.     int selected = 0; // 已选顶点数
  20.    
  21.     for (int i = 1; i <= n; ++i) {
  22.         int u = -1;
  23.         // 找到未访问的距离最小的顶点
  24.         for (int j = 1; j <= n; ++j) {
  25.             if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
  26.                 u = j;
  27.             }
  28.         }
  29.         
  30.         // 如果没有找到可达的顶点,说明图不连通
  31.         if (dist[u] == INF) {
  32.             cout << "orz" << endl;
  33.             return;
  34.         }
  35.         
  36.         visited[u] = true;
  37.         totalWeight += dist[u];
  38.         selected++;
  39.         
  40.         // 更新邻接顶点的距离
  41.         for (int v = 1; v <= n; ++v) {
  42.             if (!visited[v] && graph[u][v] < dist[v]) {
  43.                 dist[v] = graph[u][v];
  44.             }
  45.         }
  46.     }
  47.    
  48.     if (selected == n) {
  49.         cout << totalWeight << endl;
  50.     } else {
  51.         cout << "-1" << endl;  //无法构成MST。
  52.     }
  53. }
  54. int main() {
  55.     cin >> n >> m;
  56.    
  57.     // 初始化邻接矩阵
  58.     for (int i = 1; i <= n; ++i) {
  59.         for (int j = 1; j <= n; ++j) {
  60.             graph[i][j] = INF;
  61.         }
  62.     }
  63.    
  64.     // 读入边
  65.     for (int i = 0; i < m; ++i) {
  66.         int u, v, w;
  67.         cin >> u >> v >> w;
  68.         if (w < graph[u][v]) { // 处理重边,保留权重最小的
  69.             graph[u][v] = graph[v][u] = w;
  70.         }
  71.     }
  72.    
  73.     prim();
  74.    
  75.     return 0;
  76. }
复制代码
时间复杂度 \(O(m \log m)\),空间复杂度 \(O(n)\) 。
Kruskal 算法

再次观察题目,有没有什么由代价的信息呢?我们发现,添加一条边的过程实际上和并查集归并的过程如出一辙!欸,我们又找到了思路,没错,可以用并查集来寻找最小生成树。过程如下:

Code:

[code]#include #include using namespace std;const int MAXM = 2e5 + 5; // 边数上限const int MAXN = 5005;    // 点数上限struct Edge {    int u, v, w;    bool operator> n >> m;    // 初始化并查集    for (int i = 1; i > edges.u >> edges.v >> edges.w;    }    // 按边权排序    sort(edges, edges + m);    int selected = 0; // 已选边数    long long total = 0; // 总权值    // 克鲁斯卡尔主过程    for (int i = 0; i < m; i++) {        int u = edges.u, v = edges.v;        int rootU = find(u), rootV = find(v);        if (rootU != rootV) { // 不连通则归并            fa[rootU] = rootV;            total += edges.w;            selected++;            if (selected == n - 1) break; // 已选够n-1条边        }    }    // 输出结果    if (selected == n - 1) cout




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4