最小生成树 HihoCoder-1097、1098、1109(最小生成树算法)

太久没写最小生成树了,快忘光了。这几天回顾了一下

  • 最小生成树一·Prim算法
    AC G++ 369ms 17MB
    #include "cstdio"
    using namespace std;
    const int INF = 0x3f3f3f3f;
    int road[1005][1005];
    int dis[1005], n, ans;
    bool vis[1005];
    void prim() {
        int v, mn;
        for (int i = 1; i <= n; i++) {
            dis[i] = road[1][i];
        }
        vis[1] = true;
        for (int i = 1; i < n; i++) {
            mn = INF;
            for (int j = 1; j <= n; j++) {
                if (!vis[j] && dis[j] < mn) {
                    mn = dis[j];
                    v = j;
                }
            }
            ans += dis[v];
            vis[v] = true;
            for (int j = 1; j <= n; j++) {
                if (!vis[j] && dis[j] > road[v][j]) {
                    dis[j] = road[v][j];
                }
                /*
                这个if改成如下写法就变成Dijkstra算法求最短路了
                if (!vis[j] && dis[j] > dis[v] + road[v][j]) {
                    dis[j] = dis[v] + road[v][j];
                }
                */
            }
        }
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                scanf("%d", &road[i][j]);
            }
        }
        prim();
        printf("%d
    ", ans);
        return 0;
    }

    理解了Dijkstra看这个就很容易了,改一下if语句多加一个ans就是了;

  • 最小生成树二·Kruscal算法
    AC G++ 342ms 17MB
    #include "bits/stdc++.h"
    using namespace std;
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    // 并查集
    int pre[100005];
    struct Road{
        int s, e, v;
    }road[1000005];
    bool cmp(Road n, Road m) {
        return n.v < m.v;
    }
    int find(int id) {
        if (pre[id] == 0) {
            return id;
        }
        return pre[id] = find(pre[id]);
    }
    int main() {
        int n, m, ans = 0;
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d", &road[i].s, &road[i].e, &road[i].v);
        }
        sort (road, road + m, cmp);
        // 用n - 1条边可以联通n个点,所以当n == 1的时候退出循环
        for (int i = 0; n != 1; i++) {
            int s = find(road[i].s);
            int e = find(road[i].e);
            if (s != e) {
                pre[s] = e;
                n--;
                ans += road[i].v;
            }
        }
        printf("%d
    ", ans);
        return 0;
    }

    Kruscal算法有点贪心的意思在里面吧,每次取最短的边,当n个点被联通时退出循环

  • 最小生成树三·堆优化的Prim算法
    AC G++ 436ms 24MB
    #include "bits/stdc++.h"
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1e5 + 5;
    /* 
    road[i].first 存放以i为顶点的边的另一个顶点
    road[i].second 存放以i为顶点的边的长度
    */
    vector<PII> road[MAXN];
    int dis[MAXN];
    int ans;
    bool vis[MAXN];
    void prim() {
        // q表示就目前状态可以花费q.top.first的代价去联通q.top.second这个点
        priority_queue<PII, vector<PII>, greater<PII> > q;
        memset(dis, INF, sizeof(dis));
        q.push({0, 1});
        while (!q.empty()) {
            int id = q.top().second;
            if (vis[id]) {
                // 如果id已经被联通,直接continue
                q.pop();
                continue;
            } else {
                // 否则由于优先列队的排序,q.top.first一定是最小代价
                ans += q.top().first;
                q.pop();
            }
            vis[id] = true;
            for (auto i : road[id]) {
                if (i.second < dis[i.first]) {
                    dis[i.first] = i.second;
                    q.push({i.second, i.first});
                }
            }
        }
    }
    int main() {
        int n, m;
        int u, v, w;
        scanf("%d%d", &n, &m);
        while (m--) {
            scanf("%d%d%d", &u, &v, &w);
            road[u].push_back({v, w});
            road[v].push_back({u, w});
        }
        prim();
        printf("%d
    ", ans);
        return 0;
    }

    果然还是和dijkstra很像啊,仿照dijkstra的堆优化就好了。这种建图的方法倒是比原先用的链式前向星好写多了。

原文地址:https://www.cnblogs.com/Angel-Demon/p/10271234.html