求最小生成树的kruskal算法

Posted DearDongchen

tags:

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

连通无向图有最小生成树,边权从小到大排序,每次尝试加入权最小的边,如果不成圈,就把这边加进去,所有边扫一遍就求出了最小生成树。

判断连通分支用Union-Set(并查集),就是把连通的点看成一个集合,只关心哪些点在一个集合里,而不关心相互的连接方式。x父节点用fa【x】保存;如果x没有父节点,fa【x】 = x。查找一条长链的时候每次用递归把链上的点的父节点全设置成根节点,方便下次查找。思路看上去挺简单的,然而程序调试了好久。开始把边按无向图那样正反各存一次,其实是没必要的,反正每条边考察一次;剩下的就是细节问题,码力不足到处出错。测试了一组数据:

 

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxm = 20007;
struct Edge
{
    int u, v, w;
    Edge(){}
    Edge(int u, int v, int w):u(u), v(v), w(w){}
}E[maxm];//只存边就好啦,不用把从一个点出发的边穿起来
int n, m;
int r[maxm], fa[maxm];
int cmp(int x, int y)
{
    return E[x].w < E[y].w;
}
int findbaba(int x)
{
    return fa[x] == x? x : fa[x] = findbaba(fa[x]);//找父节点
}
int kruskal()
{
    int ans = 0;
    vector<int> path;//存路径
    for(int i = 0; i <= n; i++)
        fa[i] = i;//每个人的爸爸都是自己(误)
    for(int i = 0; i < m; i++)
        r[i] = i;//把边的序号放一个数组里
    sort(r, r+m, cmp);//移动序号比移动struct容易吧
    for(int i = 0; i < m; i++)
    {
        int e = r[i];
        int x = findbaba(E[e].u);
        int y = findbaba(E[e].v);
        if(x != y)//加入边e后不成圈
        {
            ans += E[e].w;
            fa[x] = y;
            path.push_back(e);
        }
    }
    for(int i = 0; i < path.size(); i++)
    {
        int e = path[i];
        printf("%d<->%d :%d ", E[e].u, E[e].v, E[e].w);
    }//打印路径
    return ans;//最小权和
}

int main()
{
    //freopen("in.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
        }
        printf("\\n%d\\n", kruskal());
    }
    return 0;
}

 

以上是关于求最小生成树的kruskal算法的主要内容,如果未能解决你的问题,请参考以下文章

求最小生成树的kruskal算法

Kruskal算法和Prim算法构造它的一棵最小代价生成树的过程

最小生成树-kruskal算法

Prim普利姆与Kruskal克鲁斯卡尔算法(Java版)

Prim算法和Kruskal算法求最小生成树

Kruskal算法求最小生成树