【洛谷】P3366 【模板】最小生成树

题目地址:

https://www.luogu.com.cn/problem/P3366

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

输入格式:
第一行包含两个整数 N , M N,M N,M,表示该图共有 N N N个结点和 M M M条无向边。接下来 M M M行每行包含三个整数 X i , Y i , Z i X_i,Y_i,Z_i Xi,Yi,Zi,表示有一条长度为 Z i Z_i Zi的无向边连接结点 X i , Y i X_i,Y_i Xi,Yi

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

数据范围:
对于 20 % 20\% 20%的数据, N ≤ 5 N≤5 N5 M ≤ 20 M≤20 M20
对于 40 % 40\% 40%的数据, N ≤ 50 N≤50 N50 M ≤ 2500 M\le 2500 M2500
对于 70 % 70\% 70%的数据, N ≤ 500 N≤500 N500 M ≤ 1 0 4 M\le 10^4 M104
对于 100 % 100\% 100%的数据: 1 ≤ N ≤ 5000 1≤N≤5000 1N5000 1 ≤ M ≤ 2 × 1 0 5 1\le M\le 2\times 10^5 1M2×105

最小生成树主要有两种算法可以做,一种是Kruskal算法,另一种是Prim算法。

Kruskal算法可以参考https://blog.csdn.net/qq_46105170/article/details/116085555。代码如下:

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 5010, M = 2e5 + 10;
int n, m;
struct Edge {
    int a, b, w;
    bool operator<(const Edge & t) const {
        return w < t.w;
    }
} e[M];
int p[N];

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

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++)
        cin >> e[i].a >> e[i].b >> e[i].w;

    sort(e, e + m);

    for (int i = 1; i <= n; i++) p[i] = i;

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i++) {
        int px = find(e[i].a), py = find(e[i].b);
        if (px != py) {
            p[px] = py;
            res += e[i].w;
            cnt++;
        }
    }

    if (cnt < n - 1) cout << "orz" << endl;
    else cout << res << endl;

    return 0;
}

时间复杂度 O ( m log ⁡ m ) O(m\log m) O(mlogm),空间 O ( n ) O(n) O(n)

Prim算法可以参考https://blog.csdn.net/qq_46105170/article/details/116084174。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 5010, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, m;
int g[N][N], dist[N];
bool st[N];

int prim() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    int res = 0;
    // n次循环,每次将一个距离已选点集合最近的点加入集合
    for (int i = 1; i <= n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!st[j] && (t == -1 || dist[j] < dist[t]))
                t = j;
        
        // 有点不可达,说明不存在最小生成树,返回-1
        if (dist[t] == INF) return -1;
        
        st[t] = true;
        res += dist[t];
        
        for (int j = 1; j <= n; j++)
            if (!st[j] && dist[j] > g[t][j])
                dist[j] = g[t][j];
    }

    return res;
}

int main() {
    cin >> n >> m;

    memset(g, 0x3f, sizeof g);
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);
    }

    int res = prim();
    if (res == -1) cout << "orz" << endl;
    else cout << res << endl;

    return 0;
}

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值