C语言实现最小生成树问题(Kruskal算法) - 乡村交通畅通工程

题目描述

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府'畅通工程'的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

输入

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。

当N为0时,输入结束,该用例不被处理。

输出

对每个测试用例,在1行里输出最小的公路总长度。

样例输入

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

样例输出

3
5

思路:Kruskal算法

Kruskal算法是一种贪心算法,用于求解最小生成树问题,可以用于本题。

具体思路如下:

  • 将所有边按照权值从小到大排序。
  • 依次选取边,若选取的边连接的两个点不在同一个连通块中,则将这条边加入最小生成树中,并将这两个点合并到同一个连通块中。
  • 当最小生成树中有N-1条边时,停止算法。

算法结束后,最小生成树的边权和即为最小的公路总长度。

代码:

#include <stdio.h>
#include <stdlib.h>

#define MAXN 100

struct Edge {
    int u, v, w;
};

int N, E;
struct Edge edges[MAXN * MAXN];
int father[MAXN];

int cmp(const void *a, const void *b) {
    return ((struct Edge *)a)->w - ((struct Edge *)b)->w;
}

void init() {
    for (int i = 1; i <= N; i++) {
        father[i] = i;
    }
}

int find(int x) {
    if (father[x] == x) {
        return x;
    } else {
        return father[x] = find(father[x]);
    }
}

void union_set(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) {
        father[fx] = fy;
    }
}

int kruskal() {
    int ans = 0, cnt = 0;
    qsort(edges, E, sizeof(struct Edge), cmp);
    for (int i = 0; i < E; i++) {
        int u = edges[i].u, v = edges[i].v, w = edges[i].w;
        if (find(u) != find(v)) {
            union_set(u, v);
            ans += w;
            cnt++;
            if (cnt == N - 1) {
                break;
            }
        }
    }
    return ans;
}

int main() {
    while (scanf('%d', &N) != EOF && N != 0) {
        E = N * (N - 1) / 2;
        for (int i = 0; i < E; i++) {
            scanf('%d %d %d', &edges[i].u, &edges[i].v, &edges[i].w);
        }
        init();
        printf('%d
', kruskal());
    }
    return 0;
}

代码解析

  • Edge 结构体用来存储每条边的信息,包括两个端点和权值。
  • init() 函数初始化并查集,每个节点的父节点初始化为自身。
  • find() 函数查找节点所属的连通块的根节点。
  • union_set() 函数合并两个连通块,将其中一个连通块的根节点设置为另一个连通块的根节点。
  • kruskal() 函数实现 Kruskal 算法,首先将所有边按照权值排序,然后依次选取边,如果这条边连接的两个点不在同一个连通块中,则将这条边加入最小生成树中,并将这两个点合并到同一个连通块中。
  • main() 函数读取输入数据,调用 kruskal() 函数计算最小生成树的边权和,并输出结果。

注意

  • 本代码中,村庄从 1 到 N 编号,如果需要从 0 到 N-1 编号,需要对代码进行相应的修改。
  • 本代码中,边权为正整数,如果需要处理负权值边,需要对代码进行相应的修改。

优化

  • 可以使用并查集优化 find() 函数的时间复杂度,例如使用路径压缩或按秩合并等技术。
  • 可以使用堆或优先队列优化边的排序操作。

希望本代码和解析能够帮助你理解 Kruskal 算法并解决乡村交通畅通工程问题。

C语言实现最小生成树问题(Kruskal算法) - 乡村交通畅通工程

原文地址: https://www.cveoy.top/t/topic/n8kI 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录