• AcWing 1141 局域网


    一、题意解析

    本题要求被除去网线的通畅程度之和最大,则要求留下来的网线通畅程度最小,也就是求图的最小生成树,由于原图不一定是连通图,所以要求的实际上是原图的最小生成森林,即若干个生成树的集合。可以用\(Prim\)\(Kruskal\)算法求解,时间复杂度为\(O(mlogm)\)

    二、Prim算法

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    
    const int N = 110;
    int w[N][N];
    int dist[N];
    bool st[N];
    int n, m, sum;
    
    int prim() {
        memset(dist, 0x3f, sizeof dist);
        dist[1] = 0;
        int res = 0;
        for (int i = 1; i <= n; i++) {
            int t = -1;
            for (int j = 1; j <= n; j++)
                if (!st[j] && (t == -1 || dist[t] > dist[j]))
                    t = j;
    
            st[t] = true;
            if (dist[t] != INF) res += dist[t]; //累加最小生成树的边权
            for (int j = 1; j <= n; j++)
                dist[j] = min(dist[j], w[t][j]);
        }
        return res;
    }
    
    int main() {
        cin >> n >> m;
        memset(w, 0x3f, sizeof w);
        for (int i = 1; i <= m; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            w[a][b] = w[b][a] = min(w[a][b], c);
            sum += c;
        }
    
        cout << sum - prim() << endl;
        return 0;
    }
    
    

    三、Kruskal算法

    简单回忆下\(kruskal\)算法。并查集中用生成树的根节点作为这个集合的表示,如果两个节点所在集合的标识相同,就处在同一个集合中。用find(int x)函数求x所在集合的标识,初始情况下各个点互不联通,所以某个节点在生成树中的父节点fa[i] = i,也就是其本身,在find函数中,一旦x == fa[x]就说明x是树的根节点,直接返回自身的值即可,否则继续对x的父节点fa[x]调用find函数,知道找到生成树的根节点。

    \(kruskal\)算法的过程是:首先按照边权对所有边从小到大排序,然后自小到大遍历边的集合,如果某条边的两个顶点\(a\)\(b\)还不在一个集合中,就将\(a\)的父节点设置为\(b\),相当于合并了这两个集合。总的代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 110, M = 210;
    int n, m, fa[N];
    //结构体
    struct Edge {
        int a, b, w;
    } e[M];
    //重载小于号,让结构体按边权进行排序,sort后由小到大排序
    bool operator<(const Edge &a, const Edge &b) {
        return a.w < b.w;
    }
    
    //并查集
    int find(int x) {
        if (fa[x] != x) fa[x] = find(fa[x]); //路径压缩
        return fa[x];
    }
    int main() {
        cin >> n >> m;
        int a, b, w;
        //并查集初始化
        for (int i = 1; i <= n; i++) fa[i] = i;
        // Kruskal算法直接记录结构体
        for (int i = 0; i < m; i++) {
            cin >> a >> b >> w;
            e[i] = {a, b, w};
        }
        sort(e, e + m); //不要忘记e数组的长度是边的数量
    
        int res = 0;
        //枚举每条边
        for (int i = 0; i < m; i++) {
            a = find(e[i].a), b = find(e[i].b), w = e[i].w;
            if (a != b)
                fa[a] = b;
            else
                res += w; //去掉的边权
        }
        cout << res << endl;
        return 0;
    }
    
  • 相关阅读:
    BZOJ 1724: [Usaco2006 Nov]Fence Repair 切割木板 贪心 + 堆 + 反向思考
    BZOJ 1715: [Usaco2006 Dec]Wormholes 虫洞 DFS版SPFA判负环
    qqq
    爬虫的盗亦有道Robots协议
    Requests库
    常用的re模块的正则匹配的表达式
    python -服务器与客户端断电续传程序详细介绍
    模拟ssh远程执行命令,粘包问题,基于socketserver实现并发的socket
    python大佬养成计划----基于flask_sqlalchemy的网页显示数据库信息
    python实战----Todo清单续写
  • 原文地址:https://www.cnblogs.com/littlehb/p/16044103.html
Copyright © 2020-2023  润新知