zoukankan      html  css  js  c++  java
  • Boruvka算法

    Boruvka算法解决某些问题超级好用。

    这些问题形如,给你n个点,每个点有点权,任意两个点之间有边权,边权为两个点权用过某种计算方式得出。

    求最小生成树。

    通常用O(log n)的时间可以找到与点i连边的边权最小的j。

    我们考虑这样一个求最小生成树的算法:

    考虑维护当前的连通块(初始每个点为独立的一个连通块)

    对每个连通块,找到一条与该连通块相连的,且另一端点不在此连通块中的边权最小的边。

    将所有的这些边都加入最小生成树,注意,当加入一条边时需判断该边的两端点是否在同一连通块。

    重复若干遍上述操作,直到图连通。

    这个算法叫Boruvka算法。

    复杂度分析:每次连通块个数至少减半,则复杂度为O((n+m)logn),还有个并查集假装是O(1)

    但是此算法通常不用于求裸的最小生成树(Kruskal多好用)

    可是在解决上述问题时,往往有奇效。

    我们发现,我们只需要求出与每个连通块相连的边权最小的边即可,在这种类型的题目中,这个东西复杂度一般为O(n log n)

    所以我们就可以在O(n log^2 n)的复杂度下解决此类问题。

    Atcoder keyence2019 E

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #define MN 201000
    
    typedef long long ll;
    int fa[MN];
    int c1[MN], c2[MN];
    int Min[MN];
    ll D; int a[MN];
    int X[MN], Y[MN];
    int x[MN], y[MN];
    ll ans = 0;
    int bl;
    int n;
    
    int Abs(int a) {return a > 0 ? a : -a;}
    ll F(int i) {return i ?  i * D + a[i] : 1e18;}
    ll G(int i) {return i ? -i * D + a[i] : 1e18;}
    ll H(int i, int j) {if(i == 0 || j == 0) return 1e18; return Abs(i - j) * D + a[i] + a[j];}
    
    void add(int *c, int x, int v, int type)
    {
        for(int i = x; i <= n; i += i & -i)
        {
            if(!type) if(F(v) < F(c[i])) c[i] = v;
            if( type) if(G(v) < G(c[i])) c[i] = v;
        }
    }
    
    int query(int *c, int x, int type)
    {
        int ans = 0;
        for(int i = x; i; i -= i & -i)
        {
            if(!type) if(F(c[i]) < F(ans)) ans = c[i];
            if( type) if(G(c[i]) < G(ans)) ans = c[i];
        }
        return ans;
    }
    
    int Find(int x) {return fa[x] == x ? x : fa[x] = Find(fa[x]);}
    
    void solve()
    {
        memset(x, 0, sizeof x);
        memset(y, 0, sizeof y); 
        memset(c1, 0, sizeof c1);
        memset(c2, 0, sizeof c2);
        memset(Min, 0, sizeof Min);
        for(int i = 1; i <= n; i++)
        {
            int u = Find(i);
            int x = query(c1, u - 1, 1);
            int y = query(c2, n - u, 1);
            if(G(x) > G(y)) x = y;
            if(H(x, i) <= H(Min[i], i)) Min[i] = x;
            add(c1, u, i, 1);
            add(c2, n - u + 1, i, 1);
        }
        memset(c1, 0, sizeof c1);
        memset(c2, 0, sizeof c2);
        for(int i = n; i >= 1; i--)
        {
            int u = Find(i);
            int x = query(c1, u - 1, 0);
            int y = query(c2, n - u, 0);
            if(F(x) > F(y)) x = y;
            if(H(x, i) <= H(Min[i], i)) Min[i] = x;
            add(c1, u, i, 0);
            add(c2, n - u + 1, i, 0);
        }
        for(int i = 1; i <= n; i++)
        {
            int u = Find(i);
            if(H(i, Min[i]) < H(x[u], y[u])) x[u] = i, y[u] = Min[i];
        }
        int tot = 0;
        for(int i = 1; i <= n; i++)
        {
            int u = Find(i);
            ++tot; X[tot] = x[u]; Y[tot] = y[u];
        }
        for(int i = 1; i <= tot; i++)
        {
            int x = Find(X[i]), y = Find(Y[i]);
            if(x == y) continue;
            ans += H(X[i], Y[i]);
            fa[x] = y;
            bl--;
        }
    }
    
    int main()
    {
        scanf("%d%lld", &n, &D);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]), fa[i] = i;
        bl = n;
        while(bl > 1) solve();
        printf("%lld
    ", ans);
    }
  • 相关阅读:
    C#开源实现MJPEG流传输
    EntityFramework中使用Repository装饰器
    Lambda应用设计模式
    Lambda表达式的前世今生
    那些年黑了你的微软BUG
    敏捷软件开发揭秘
    SVN previous operation has not finished
    NodeJS+Express开发web,为什么中文显示为乱码
    使用Visual Studio 调试断点不起作用的问题解决办法 调试Revit CAD 不能进入断点
    openFileDialog的Filter属性设置
  • 原文地址:https://www.cnblogs.com/lher/p/10393232.html
Copyright © 2011-2022 走看看