• 最小生成树两连


    最小生成树两连

    并查集优化的克鲁斯卡尔算法和优先队列+链式前向星优化的普利姆算法

    Kruskal

    Kruskal是常用的最小生成树算法,算法利用贪心思想,每次选择没用过且不构成环的边的最小边,直到选择了n-1条边,通常我们用并查集这个数据结构去优化,优化后的Kruskal算法复杂度是(O(mlogm+malpha m)),复杂度只和边的数量有关,所以适用于稀疏图

    #include <bits/stdc++.h>
    using namespace std;
    int n;                     // n个点
    int m;                     // m条边
    int ans;                   //最小生成树的权值
    const int MAXM = 2e5 + 5;  //边的数据范围
    const int MAXN = 5005;     //点的数据范围
    
    struct node {
        int u;
        int v;
        int w;
    } e[MAXM];
    
    bool cmp(node a, node b) { return a.w < b.w; }
    int parent[MAXN];
    int rnk[MAXN];
    void init() {
        ans = 0;
        for (int i = 0; i <= n; i++) {
            rnk[i] = 0;
            parent[i] = i;
        }
    }
    int find(int x) {
        if (parent[x] == x) {
            return x;
        } else {
            return parent[x] = find(parent[x]);  //路径压缩
        }
    }
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return;
        }
        if (rnk[x] < rnk[y]) {  //按秩合并
            parent[x] = y;
        } else {
            parent[y] = x;
            if (rnk[x] == rnk[y]) {
                rnk[x]++;
            }
        }
    }
    bool issame(int x, int y) { return find(x) == find(y); }
    void kruskal(void) {
        ans = 0;
        int cnt = 0;                    // 记录已经选用的边数
        for (int i = 1; i <= m; i++) {  //枚举m条边
            //判断是否在一个集合中
            if (!issame(e[i].u, e[i].v)) {
                //没在集合中,就选中这条边,把u,v两点连起来
                unite(e[i].u, e[i].v);
                ans += e[i].w;
                cnt++;
            }
            if (cnt == n - 1) {  //选到了n-1条边了
                break;
            }
        }
    }
    int main(void) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
        }
        sort(e + 1, e + m + 1, cmp);  //按照边的权值排序
        init();
        kruskal();
        printf("%d
    ", ans);
        return 0;
    }
    

    Prim

    Prim是针对点的最小生成树算法,任意选一个点出发,构成一个集合V,然后每次选择距离这个集合最近的点加入,当所有点都被加入集合V中时得到这棵树。使用优先队列和链式前向星优化的Prim算法的时间复杂度是(O((m+n)logm)),适用于稠密图

    #include <bits/stdc++.h>
    using namespace std;
    int n;                     // n个点
    int m;                     // m条边
    int ans;                   //最小生成树的权值
    int cnt;                   //边的index
    const int MAXM = 2e5 + 5;  //边的数据范围
    const int MAXN = 5005;     //点的数据范围
    
    struct node {
        int from;  //和这条边同起点的上一条边的存储位置
        int to;    //这条边的终点
        int w;
        friend bool operator<(node a, node b) { return a.w > b.w; }
    } e[MAXM << 1];  //无向图开两倍边
    
    int head[MAXN];  // head[i]表示最后出现的以i为起点的边
    bool vis[MAXN];  //访问过了的点
    void add(int u, int v, int w) {
        e[cnt].from = head[u];  //记录上次出现的边
        e[cnt].to = v;
        e[cnt].w = w;
        head[u] = cnt++;  //更新本次出现的边,然后cnt++
    }
    priority_queue<node> q;
    void init() {
        while (!q.empty()) q.pop();
        memset(head, 0, sizeof(head));
        memset(vis, 0, sizeof(vis));
        cnt = 1;
        ans = 0;
    }
    void prim() {
        int now = 1;      //从点1开始拓展
        int tot = n - 1;  //最多找n-1次
        while (tot--) {
            vis[now] = true;  //记录已经访问过的点
            //找以now点出发的所有边,将终点没有被访问的加入队列
            for (int i = head[now]; i != 0; i = e[i].from) {
                if (!vis[e[i].to]) {
                    q.push(e[i]);
                }
            }
            node t = q.top();  //从当前的可拓展边中找出一条最短的
            q.pop();
            while (vis[t.to]) {  //弹出所有已经被访问过的边
                t = q.top();
                q.pop();
            }
            now = t.to;
            ans += t.w;
        }
    }
    int main(void) {
        scanf("%d%d", &n, &m);
        init();
        int u, v, w;
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        prim();
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    取石子(二)
    Nim游戏 之HDU 1850 Being a Good Boy in Spring Festival
    移动字母
    asterisk meetme 会议实现
    asterisk基础学习一
    Asterisk 1.8 sip 协议栈分析
    asterisk dialplan详解
    asterisk chan_sip.c代码分析
    asteirsk 开发指南
    asterisk 基础学习二
  • 原文地址:https://www.cnblogs.com/hermitgreen/p/12652586.html
Copyright © 2020-2023  润新知