【解题思路】
该题难点是要保存最小生成树的所有边,对于不同的求最小生成树的算法,有不同的方法来完成。
【参考代码】
prim算法
#include<bits/stdc++.h>
using namespace std;
const int N = 105; // 定义常量 N,表示数组大小
int n, e, x, y, w;
int g[N][N]; // 保存图的邻接矩阵
int ans[N]; // 记录当前最小生成树中每个点的父亲节点编号
bool v[N]; // 标记哪些点已经在MST中
struct nod {
int to, w;
bool operator < (const nod &b) const { // 重载小于号运算符,使得该结构体对象可被优先队列使用(小根堆)
return w > b.w; // 按权值从小到大排序
}
};
void prim() {
priority_queue<nod> pq;
pq.push({1, 0}); // 将结点 1 加入最小生成树中,其父亲节点为 0
while (!pq.empty()) {
nod t = pq.top();
pq.pop();
if (v[t.to]) continue; // 如果已经在最小生成树中,则忽略
v[t.to] = true; // 标记该结点已加入MST
if (t.w != 0) { // 输出点与其父亲节点之间的边
cout << ans[t.to] << " " << t.to << endl;
}
for (int i = 1; i <= n; i++) { // 枚举所有未访问过的结点
if (!v[i] && g[t.to][i] < 0x3f3f3f3f) { // 如果可从 t.to 到达i,并且 i 未被访问过
pq.push({i, g[t.to][i]}); // 将 i 加入优先队列
if (g[t.to][i] < g[ans[i]][i]) ans[i] = t.to; // 更新当前最短距离
}
}
}
}
int main() {
cin >> n >> e;
memset(g, 0x3f, sizeof g); // 初始化邻接矩阵为正无穷
for (int i = 1; i <= e; i++) {
cin >> x >> y >> w;
g[x][y] = g[y][x] = w; // 读入边信息,建立双向边
}
for (int i = 1; i <= n; i++) ans[i] = i; // 最开始每个点的父亲节点都是自己
prim(); // 运行 Prim 算法求解最小生成树
return 0;
}
Kruskal算法
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAX_N = 100;
const int MAX_M = 10000;
int n, m;
struct Edge {
int u, v;
int len;
};
vector<Edge> MST; // 用于存储最小生成树的边
Edge E[MAX_M]; // 存储所有边的数组
int fa[MAX_N]; // 并查集的父节点数组
bool cmp(Edge a, Edge b) { // 按边长从小到大排序
return a.len < b.len;
}
void init() {
for (int i = 1; i <= n; i++) {
fa[i] = i;
}
}
int find(int x) { // 路径压缩
while (fa[x] != x) x = fa[x];
return x;
}
void unionn(int x, int y) { // 合并集合
fa[y] = x;
}
int kruskal() {
int sum = 0; // 最小生成树的权值和
init(); // 初始化并查集
sort(E, E + m, cmp); // 对边进行排序
for (int i = 0; i < m; i++) {
int u = find(E[i].u);
int v = find(E[i].v);
if (u != v) { // 如果u和v不在同一个集合中
unionn(u,v); // 合并集合
MST.push_back({E[i].u, E[i].v, E[i].len}); // 将边加入到最小生成树中
sum += E[i].len; // 更新权值和
}
}
return sum; // 返回最小生成树的权值和
}
int main() {
cin >> n >> m; // 输入顶点数和边数
for (int i = 0; i < m; i++) { // 输入每条边的信息
cin >> E[i].u >> E[i].v >> E[i].len; // 输入起点、终点和长度,并存储到边数组中
}
int totalCost = kruskal(); // 调用Kruskal算法计算最小生成树的权值和
for (int i=0;i<n-1;i++) { // 遍历最小生成树中的边
cout << MST[i].u << " " << MST[i].v << endl; // 输出边的信息
}
return 0; // 程序结束返回0
}