码迷,mamicode.com
首页 > 编程语言 > 详细

Boruvka算法

时间:2019-02-17 23:31:48      阅读:306      评论:0      收藏:0      [点我收藏+]

标签:注意   需要   ||   size   ring   color   scan   类型   nbsp   

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\n", ans);
}

 

Boruvka算法

标签:注意   需要   ||   size   ring   color   scan   类型   nbsp   

原文地址:https://www.cnblogs.com/lher/p/10393232.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!