zoukankan      html  css  js  c++  java
  • 最小生成树(Kruskal & Prim & Boruvka)

    例题

    kruskal

    • Kruskal算法通过并差集维护,从到小枚举每条边,如果两端点不在一个集合,将两端点所在集合合并,并将边权累加到答案中

    • 时间复杂度为(O(m log m))

    • 评测记录

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 5005, M = 2e5+5;
    struct side {
        int x, y, d;
    }e[M];
    bool operator < (side a, side b) {
        return a.d < b.d;
    }
    int n, m, f[N], ans, cnt;
    int found(int x) {
        return x == f[x] ? x : (f[x] = found(f[x]));
    }
    void kruskal() {
        for (int i = 1; i <= m; i++) {
            int x = found(e[i].x), y = found(e[i].y);
            if (x == y) continue;
            f[x] = y;
            ans += e[i].d;
            if (++cnt == n-1) return;//最小生成树上最多n-1条边
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].d);
        sort(e + 1, e + m + 1);//排序
        for (int i = 1; i <= n; i++) f[i] = i;//并查集初始化
        kruskal();
        if (cnt == n-1) printf("%d
    ", ans);
        else puts("orn");//奇奇怪怪的输出,其实他没有这个测试点
        return 0;
    }
    

    prim

    • Prim算法思想类似于Dijkatra。
      维护一个数组 d[x] 节点 x 与已确定是最小生成树集合中的节点之间权值最小的边的权值
      每次从未标记的节点中选出 d 值最小的点,将其标记,在更新其他点的 d 值

    • 时间复杂度为(O(n^2)),使用堆优化可以到(O(mlogn))(这里写的是堆优化后的板子) 但是这样不如Kruskal方便,
      因此,Prim主要用于稠密图,尤其是完全图的求解

    • 评测记录

    #include <queue>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N = 5005, M = 2e5+5;
    struct side{
        int t, d, next;
    }e[M<<1];
    int head[N], tot;
    void add(int x, int y, int z) {
        e[++tot].next = head[x];
        head[x] = tot;
        e[tot].t = y;
        e[tot].d = z;
    }
    priority_queue< pair<int, int> > q;
    int n, m, d[N], ans, cnt;
    bool v[N];
    void prim() {
        memset(d, 0x3f, sizeof(d));
        q.push(make_pair(d[1] = 0, 1));
        while (!q.empty() && cnt < n) {
            int x, w;
            x = q.top().second;
            w = -q.top().first;
            q.pop();
            if (v[x]) continue;
            v[x] = 1;
            ans += w;
            cnt++;
            for (int i = head[x]; i; i = e[i].next) {
                int y = e[i].t;
                if (d[y] > e[i].d) {
                    d[y] = e[i].d;
                    q.push(make_pair(-d[y], y));
                }
            }
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
        while (m--) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z); add(y, x, z);
        }
        prim();
        if (cnt == n) printf("%d
    ", ans);
        else puts("orn");
        return 0;
    }
    

    Boruvka

    • 思路是对于每个联通块找一条连向其他联通块的边权最小的边,然后合并这两个联通块

    • 每次操作都会至少减少一半的联通块,每次需要o(m)得遍历每条边,最多便利logn次,时间复杂度 (O(m log n))

    • 复杂度瓶颈在于如何找联通块之外边权最小的边,在一些有特殊性质的题中可以用数据结构或性质进行优化

    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    const int N = 2e5 + 5;;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    int n, m, p[N], w[N], f[N], cnt, ans;
    
    struct Side {
        int x, y, d;
    }a[N];
    
    int Find(int x) {
        return x == f[x] ? x : f[x] = Find(f[x]);
    }
    
    int main() {
        n = read(); m = read();
        for (int i = 1; i <= m; ++i)
            a[i] = (Side) {read(), read(), read()};
        for (int i = 1; i <= n; ++i) f[i] = i;
        while (cnt < n - 1) {
            memset(p + 1, 0, n * 4);
            memset(w + 1, 0x3f, n * 4);
            for (int i = 1; i <= m; ++i) {
                int x = Find(a[i].x), y = Find(a[i].y), d = a[i].d;
                if (x == y) continue;
                if (d < w[x]) w[x] = d, p[x] = y;
                if (d < w[y]) w[y] = d, p[y] = x;
            }
            for (int i = 1; i <= n; ++i) {
                if (i != Find(i)) continue;
                int x = Find(p[i]);
                if (i == x) continue;
                f[i] = x, ans += w[i], cnt++;
            }
        }
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    halcon 如何把一个region截取出来保存为图像
    Halcon学习(三)赋值与数组操作
    sort_region——对区域进行排序
    Halcon函数【转】
    Halcon算子之shape_trans,用于变换区域的形状
    Halcon学习之八:图像区域叠加与绘制
    Halcon学习之七:改变图像的现实方式和大小
    Halcon学习之六:获取Image图像中Region区域的特征参数
    Halcon学习之五:有关图像的定义域的函数
    Docker Swarm redis 集群搭建
  • 原文地址:https://www.cnblogs.com/shawk/p/13229244.html
Copyright © 2011-2022 走看看