《P3366 【模板】最小生成树》

发布于:2025-08-15 ⋅ 阅读:(16) ⋅ 点赞:(0)

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式

第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi​,Yi​,Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi​,Yi​。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

输入输出样例

输入 #1复制

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

输出 #1复制

7

说明/提示

数据规模:

对于 20% 的数据,N≤5,M≤20。

对于 40% 的数据,N≤50,M≤2500。

对于 70% 的数据,N≤500,M≤104。

对于 100% 的数据:1≤N≤5000,1≤M≤2×105,1≤Zi​≤104。

样例解释:

所以最小生成树的总边权为 2+2+3=7。

代码实现:

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

using namespace std;

// 边的结构:起点、终点、权值
struct Edge {
    int u, v, w;
    Edge(int u_, int v_, int w_) : u(u_), v(v_), w(w_) {}
    
    // 用于排序,按权值从小到大
    bool operator<(const Edge& other) const {
        return w < other.w;
    }
};

// 并查集数据结构,用于判断连通性
class UnionFind {
private:
    vector<int> parent;
    vector<int> rank;
    
public:
    UnionFind(int n) {
        parent.resize(n + 1);  // 节点编号从1开始
        rank.resize(n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            parent[i] = i;
        }
    }
    
    // 查找根节点
    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);  // 路径压缩
        }
        return parent[x];
    }
    
    // 合并两个集合
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        
        if (x == y) return;  // 已经在同一个集合
        
        // 按秩合并
        if (rank[x] < rank[y]) {
            parent[x] = y;
        } else {
            parent[y] = x;
            if (rank[x] == rank[y]) {
                rank[x]++;
            }
        }
    }
    
    // 判断两个节点是否连通
    bool isConnected(int x, int y) {
        return find(x) == find(y);
    }
};

int main() {
    int N, M;
    cin >> N >> M;
    
    vector<Edge> edges;
    for (int i = 0; i < M; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        edges.push_back(Edge(x, y, z));
    }
    
    // 按权值排序
    sort(edges.begin(), edges.end());
    
    UnionFind uf(N);
    int totalWeight = 0;
    int edgesUsed = 0;
    
    // 使用传统for循环遍历所有边,构建最小生成树
    for (int i = 0; i < edges.size(); ++i) {
        const Edge& e = edges[i];
        if (!uf.isConnected(e.u, e.v)) {
            uf.unite(e.u, e.v);
            totalWeight += e.w;
            edgesUsed++;
            
            // 最小生成树有n-1条边
            if (edgesUsed == N - 1) {
                break;
            }
        }
    }
    
    // 检查是否所有节点都连通
    if (edgesUsed == N - 1) {
        cout << totalWeight << endl;
    } else {
        cout << "orz" << endl;
    }
    
    return 0;
}
    


网站公告

今日签到

点亮在社区的每一天
去签到