本专栏持续输出数据结构题目集,欢迎订阅。
题目
给定一个带权无向图,如果是连通图,则至少存在一棵最小生成树,有时最小生成树并不唯一。本题就要求你计算最小生成树的总权重,并且判断其是否唯一。
输入格式:
首先第一行给出两个整数:无向图中顶点数 N(≤500)和边数 M。随后 M 行,每行给出一条边的两个端点和权重,格式为“顶点1 顶点2 权重”,其中顶点从 1 到N 编号,权重为正整数。题目保证最小生成树的总权重不会超过 2^30。
输出格式:
如果存在最小生成树,首先在第一行输出其总权重,第二行输出“Yes”,如果此树唯一,否则输出“No”。如果树不存在,则首先在第一行输出“No MST”,第二行输出图的连通集个数。
输入样例 1:
5 7
1 2 6
5 1 1
2 3 4
3 4 3
4 1 7
2 4 2
4 5 5
输出样例 1:
11
Yes
输入样例 2:
4 5
1 2 1
2 3 1
3 4 2
4 1 2
3 1 3
输出样例 2:
4
No
输入样例 3:
5 5
1 2 1
2 3 1
3 4 2
4 1 2
3 1 3
输出样例 3:
No MST
2
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_N 501
#define MAX_M 125000 // 最大边数:500*499/2 ≈ 124750
#define INF 0x3f3f3f3f
// 边的结构体
typedef struct {
int u, v, weight;
} Edge;
// 并查集结构
int parent[MAX_N];
// 查找根节点(带路径压缩)
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
// 比较函数:按权重升序排序,权重相同则按顶点对升序排序
int compareEdges(const void* a, const void* b) {
Edge* e1 = (Edge*)a;
Edge* e2 = (Edge*)b;
if (e1->weight != e2->weight) {
return e1->weight - e2->weight;
}
if (e1->u != e2->u) {
return e1->u - e2->u;
}
return e1->v - e2->v;
}
// Kruskal算法计算最小生成树权重,并收集关键边
int kruskal(Edge* edges, int m, int n, Edge* mstEdges, int* mstSize) {
// 初始化并查集
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
// 排序边
qsort(edges, m, sizeof(Edge), compareEdges);
int totalWeight = 0;
*mstSize = 0;
// 遍历所有边,构建最小生成树
for (int i = 0; i < m; i++) {
int u = edges[i].u;
int v = edges[i].v;
int w = edges[i].weight;
int rootU = find(u);
int rootV = find(v);
if (rootU != rootV) {
parent[rootU] = rootV;
totalWeight += w;
mstEdges[*mstSize] = edges[i];
(*mstSize)++;
}
}
return totalWeight;
}
// 检查最小生成树是否唯一
int isUnique(Edge* edges, int m, int n, int mstWeight, Edge* mstEdges, int mstSize) {
// 尝试移除每条 MST 边,检查是否还能形成相同权重的 MST
for (int i = 0; i < mstSize; i++) {
// 初始化并查集
for (int j = 1; j <= n; j++) {
parent[j] = j;
}
int currentWeight = 0;
int edgesUsed = 0;
// 遍历所有边,但跳过当前要移除的 MST 边
for (int j = 0; j < m; j++) {
// 跳过当前要移除的 MST 边
if (edges[j].u == mstEdges[i].u && edges[j].v == mstEdges[i].v &&
edges[j].weight == mstEdges[i].weight) {
continue;
}
int u = edges[j].u;
int v = edges[j].v;
int w = edges[j].weight;
int rootU = find(u);
int rootV = find(v);
if (rootU != rootV) {
parent[rootU] = rootV;
currentWeight += w;
edgesUsed++;
if (edgesUsed == n - 1) {
break;
}
}
}
// 如果能形成相同权重的 MST,则原 MST 不唯一
if (edgesUsed == n - 1 && currentWeight == mstWeight) {
return 0; // 不唯一
}
}
return 1; // 唯一
}
// 计算图的连通分量数量
int countConnectedComponents(Edge* edges, int m, int n) {
// 初始化并查集
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
// 遍历所有边,合并连通分量
for (int i = 0; i < m; i++) {
int u = edges[i].u;
int v = edges[i].v;
int rootU = find(u);
int rootV = find(v);
if (rootU != rootV) {
parent[rootU] = rootV;
}
}
// 统计连通分量数量
int count = 0;
for (int i = 1; i <= n; i++) {
if (parent[i] == i) {
count++;
}
}
return count;
}
int main() {
int N, M;
scanf("%d %d", &N, &M);
Edge edges[MAX_M];
for (int i = 0; i < M; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
// 确保u < v,方便后续处理
if (u > v) {
int temp = u;
u = v;
v = temp;
}
edges[i].u = u;
edges[i].v = v;
edges[i].weight = w;
}
// 检查图是否连通,并计算最小生成树
Edge mstEdges[MAX_N]; // 存储MST的边
int mstSize;
int mstWeight = kruskal(edges, M, N, mstEdges, &mstSize);
// 判断是否存在MST
if (mstSize != N - 1) {
// 不存在MST,计算连通分量数量
int components = countConnectedComponents(edges, M, N);
printf("No MST\n");
printf("%d\n", components);
} else {
// 存在MST,判断是否唯一
int unique = isUnique(edges, M, N, mstWeight, mstEdges, mstSize);
printf("%d\n", mstWeight);
printf("%s\n", unique ? "Yes" : "No");
}
return 0;
}
7432

被折叠的 条评论
为什么被折叠?



