求krusal算法原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求krusal算法原理相关的知识,希望对你有一定的参考价值。

如题,,,,,,,,

参考技术A kruskal算法构造G的最小生成树的思想是,首先将G的n个顶点看成是n个孤立的连通分支,将所有的边按权从小到大排序,然后从第一条边开始,依边权递增的顺序查看每一边,并按下述方法连接两个不同的连通分支:当查看到第k条边(v,w)时,如果端点v,w分别是当前两个不同的连通分支T1和T2中的顶点时,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边。这个过程就是G的一棵最小生成树。

最小生成树--Boruvka算法

参考文章

介绍

第一次听说这个算法。。
对于最小生成树一定学过prim和krusal,prim复杂度是 O ( n 2 ) 或 者 O ( e l o g n ) O(n^2)或者O(elogn) O(n2)O(elogn),krusal复杂度是 O ( e l o g e ) O(eloge) O(eloge),这里介绍一下Boruvka算法
Boruvka算法解决某些特定问题非常好用:
给定n个点,每个点都有点权,任意两个点之间有边权,边权为两个点权用过某种计算方式得出(例如两点权之差),求最小生成树
点的数量为n,边的数量为n^2,当n=1e5,prim和Krusal都会超时,现在用Boruvka求最小生成树的算法:
考虑维护当前的连通块(初始每个点为独立的一个连通块)
对于每个连通块,找到一条与该连通块相连的,且另一端点不在此连通块中的边权最小的边
将所有的这些边加入到最小生成树,注意,当加入一个边时需要判断该点的两端点是否在同一连通块内。
重复若干遍上述操作,直到图连通
复杂度分析:每次连通块的个数至少减半,复杂度为O((n+m)logn),并查集操作是O(1)
本算法不常用于求裸的最小生成树(因为Krusal更好用)
在给出的题目情况中,我们只需要求出每个连通块相连的边权的最小边即可,在这种类型的题目中,这个东西的复杂度一般是O(nlogn)
所以可以在 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的复杂度下解决此类问题

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
inline int read()
{
    int x= 0;
    int w= 0;
    char ch= 0;
    while (ch < '0' || ch > '9')
        w|= ch == '-', ch= getchar();
    while (ch >= '0' && ch <= '9')
        x= (x << 3) + (x << 1) + (ch ^ 48), ch= getchar();
    return w ? (-x) : x;
}
#define mp make_pair
const int N= 200005;
const int M= 500005;
const LL inf= 1e12;
int f[N], pd, n, m;
struct node
{
    int a, b;
    LL c;
} e[M];
pair<LL, LL> E[N];
int find(int k)
{
    return (k == f[k]) ? k : f[k]= find(f[k]);
}
LL Boruvka()
{
    LL res= 0;
    pd= 1;
    int num= 0;
    for (int i= 1; i <= n; i++)
        f[i]= i;
    while (num < n - 1) {
        int tmp= 0;
        for (int i= 1; i <= n; i++)
            E[find(i)]= mp(inf, inf);
        for (int i= 1; i <= m; i++) {
            int fa= find(e[i].a);
            int fb= find(e[i].b);
            if (fa == fb)
                continue;
            tmp++;
            //取最小的边,最小边一样取最小编号 
            E[fa]= min(E[fa], mp(e[i].c, i * 1ll)); 
            E[fb]= min(E[fb], mp(e[i].c, i * 1ll));
        }
        if (tmp == 0)
            break;
        for (int i= 1; i <= m; i++) {
            int fa= find(e[i].a);
            int fb= find(e[i].b);
            if (fa == fb)
                continue;
            if ((E[fa] == mp(e[i].c, i * 1ll)) || (E[fb] == mp(e[i].c, i * 1ll))) {
                f[fa]= fb;
                res+= e[i].c;
                num++;
            }
        }
    }
    if (num < n - 1)
        pd= 0;
    return res;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i= 1; i <= m; i++)
        scanf("%d%d%lld", &e[i].a, &e[i].b, &e[i].c);
    LL ans= Boruvka();
    if (!pd)
        printf("orz\\n");
    else
        printf("%lld\\n", ans);
    return 0;
}

题目:

CF1550F Jumping Around

以上是关于求krusal算法原理的主要内容,如果未能解决你的问题,请参考以下文章

POJ1523(求连用分量数目,tarjan算法原理理解)

EM算法原理以及高斯混合模型实践

KM算法原理+证明

支持向量机原理SMO算法原理

浅谈Stein算法求最大公约数(GCD)的原理及简单应用

对求有向图强连通分量的tarjan算法原理的一点理解