本文共 1933 字,大约阅读时间需要 6 分钟。
给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负数。求最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。以下是详细的步骤说明和代码实现。
Kruskal算法是一种广泛用于求解最小生成树的高效方法,尤其适用于稀疏图。其核心思想可以总结为以下几个步骤:
排序边:将所有边按权重从小到大进行排序。这一步的时间复杂度为O(m log m),因为需要对m条边进行排序。
并查集初始化:使用并查集数据结构来跟踪每个顶点所属的连通分量。并查集的路径压缩和按秩合并操作能够保证操作的高效性,时间复杂度分别为O(α(m))和O(m),其中α是阿克曼函数的反函数,非常接近常数。
遍历边并选择边:按权重从小到大遍历每一条边,检查其两个端点是否在同一个连通分量中:
判断生成树是否存在:在遍历完所有边后,检查生成树中包含的顶点数是否等于n。如果等于,说明存在最小生成树,输出总权重;如果不等于,说明图中存在环,无法形成生成树,输出impossible。
Kruskal算法的时间复杂度主要由两部分决定:
因此,Kruskal算法的总时间复杂度为O(m log m),适用于处理较大的稀疏图。
以下是使用C++语言实现的Kruskal算法的代码:
#includeusing namespace std;const int N = 100, M = 200;int p[N];int n, m;struct edge { int a, b, w; bool operator<(const edge &e) const { return w < e.w; }};int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x];}int kruskal() { int res = 0, cnt = 1; sort(e + 1, e + m + 1); for (int i = 1; i <= m; ++i) { int a = e[i].a, b = e[i].b, w = e[i].w; a = find(a), b = find(b); if (a != b) { p[a] = b; res += w; cnt++; } } return cnt == n ? res : -1;}int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { p[i] = i; } for (int i = 1; i <= m; ++i) { int a, b, w; scanf("%d%d%d", &a, &b, &w); e[i] = {a, b, w}; } int t = kruskal(); if (t == -1) { puts("impossible"); } else { printf("%d\n", t); } return 0;}
数据结构定义:
struct edge
定义了边的结构,包含两个顶点和权重,并支持按权重排序。int p[N];
用于并查集操作,记录每个顶点的父节点。并查集函数:
int find(int x);
实现路径压缩,找到x的根节点。Kruskal主函数:
输入处理:
输出结果:
通过以上方法,可以有效地求解给定图的最小生成树问题。Kruskal算法的高效性和代码的简洁性使其成为处理此类问题的理想选择。
转载地址:http://ujlwz.baihongyu.com/