• 最小生成树(MST)[简述][模板]


    Prim(添点法)

    1. 任选一点(一般选1), 作为切入点,设其与最小生成树的距离为0(实际上就是选一个点,将此树实体化),.

    2. 在所有未选择的点中选出与最小生成树距离最短的, 累计其距离, 并标为已选. 若都选择了, 则得到了最小生成树(的总路长).

    3. 更新与此点相邻的点"与最小生成树的距离".返回2.

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    using namespace std;
    #define N 1003
    #define inf 0x3f3f3f3f
    struct node {
        int v, w;
        node () {}
        node(int _v, int _w) : v(_v), w(_w) {}
    };
    vector<node> g[N];
    int n, m, d[N];
    bool vis[N];
    
    int prim() {
        memset(vis, false, sizeof(vis));
        memset(d, 0x3f, sizeof(d));
        int ans = d[1] = 0;
        for (int i=0; i<n; i++) {
            int k = 0, mi = inf;
            for (int j=1; j<=n; j++) if (!vis[j] && d[j] < mi)
                mi = d[j], k = j;
            if (k == 0) break;
            vis[k] = true;
            ans += mi;
            for (int j=0, u; j<g[k].size(); j++)
                if (!vis[u = g[k][j].v] && d[u] > g[k][j].w)
                    d[u] = g[k][j].w;///和Dijkstra很像,只是这里由松弛操作改成了更新
        }///此处的d表示与树的距离
        return ans;///返回的是最小生成树的边长和
    }
    int main() {
    
        while (scanf("%d%d", &n, &m) == 2) {
            for (int i=0; i<=n; i++) g[i].clear();
            for (int i=0, a, b, c; i<m; i++) {
                scanf("%d%d%d", &a, &b, &c);
                g[a].push_back(node(b, c));
                g[b].push_back(node(a, c));
            }
            printf("%d
    ", prim());
        }
    
        return 0;
    }
    

    Kruskal(添边法)

    1. 将所有点加入并查集, 每个点都是独立的集合

    2. 将所有边按长度排序.

    3. 拿出最小边, 判断两顶点是否在同一集合, 直到边集为空. 若是, 舍弃, 返回3. 若不是, 将左顶点加入右顶点之集合(左右都是无所谓的~), 返回3.

    4. 若边集为空,则得到最小生成树.

    并查集按惯例使用路径压缩.

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    #define N 1002
    struct node {
        int u, v, w;
        node() {}
        node(int _u, int _v, int _w):u(_u), v(_v), w(_w) {}
    };
    vector<node> edge;
    int n, m, f[N];
    bool cmp(const node &x, const node &y) {
        return x.w < y.w;
    }
    int find_set(int x) {
        if (f[x] == x) return x;
        return f[x] = find_set(f[x]);
    }
    
    int Kruskal() {
        sort(edge.begin(), edge.end(), cmp);
        for (int i=1; i<=n; i++) f[i] = i;///将所有点加入并查集,自己是一个独立的集合
        int ans = 0;
        for (int i=0, u, v, w; i<edge.size(); i++) {///排序之后只要挨着拿就行
            u = edge[i].u, v = edge[i].v, w = edge[i].w;
            u = find_set(u), v = find_set(v);
            if (u == v) continue;
            f[u] = v;///这个是随便的,虽然会引起效率上的不稳定,但是一次路径压缩之后就都一样了..
            ans += w;
        }
        return ans;
    }
    int main() {
        while (scanf("%d%d", &n, &m) == 2) {
            edge.clear();
            for (int i=0, a, b, c; i<m; i++) {
                scanf("%d%d%d", &a, &b, &c);
                edge.push_back(node(a, b, c));///两端点是平等的,插入一次即可
            }
            printf("%d
    ", Kruskal());
        }
        return 0;
    }
    


  • 相关阅读:
    题解——[[SHOI2010]最小生成树]
    7.12周总结
    还有5个月就NOIP2019了,我干了什么
    【CQOI2018】破解D-H协议
    【SHOI2006】仙人掌
    【HNOI/AHOI2018】道路
    2019.11纪中集训 宋新波老师和曹天佑学长的勉励
    纪中集训2019.11.05
    【2019.10.25 OI-Killer的模拟赛】3.鸡数
    【华东师附国庆模拟赛】Day2 1.矩阵
  • 原文地址:https://www.cnblogs.com/riskyer/p/3266495.html
Copyright © 2020-2023  润新知