📅  最后修改于: 2023-12-03 15:41:09.353000             🧑  作者: Mango
在图论中,给定一个加权无向连通图,生成树是指原图的一个连通子图,且所有顶点都在其中,它是一棵树。如果给每条边赋上一个权值,生成树的权值就是所有边的权值之和,最小生成树(MST,Minimum Spanning Tree)就是指生成树的权值最小的一颗。
一个图可以有多个不同的最小生成树,但如果每条边的权值都是唯一的,则最小生成树是唯一的。
常见的求解最小生成树的算法有 Prim 算法和 Kruskal 算法。
Prim 算法属于贪心算法,从一个初始点开始,通过不断加入连通着已有部分的最小边来构造生成树。具体步骤如下:
Prim 算法的时间复杂度为 O(n^2),其中 n 为顶点数,在稠密图中表现优异,在稀疏图中会更劣。
Kruskal 算法也是贪心算法,它通过合并连通块来构造生成树,具体步骤如下:
Kruskal 算法的时间复杂度为 O(eloge),其中 e 为边数,在稀疏图中表现优异,在稠密图中会更劣。
最小生成树算法可以用于无向加权图的建模与处理,可以通过 C++ 实现。其中,我们可以使用邻接矩阵或邻接表来实现图。
const int INF = 1000000000; // 表示无穷大
int main() {
int n; // n 为顶点数
int G[MAX_V][MAX_V]; // 邻接矩阵
bool used[MAX_V]; // 记录顶点是否已经加入生成树
int d[MAX_V]; // 记录生成树中与顶点最小边的权值
// 初始化邻接矩阵和 used、d 数组
for (int i = 0; i < n; i++) {
fill(G[i], G[i] + n, INF);
used[i] = false;
d[i] = INF;
}
// 读入边
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
G[u][v] = w;
G[v][u] = w;
}
// Prim 算法求解最小生成树
long long res = 0; // 最小生成树的边权之和
d[0] = 0; // 从任意一个点开始
while (true) {
int v = -1;
// 找到未使用中与某个点距离最小的点
for (int u = 0; u < n; u++) {
if (!used[u] && (v == -1 || d[u] < d[v])) {
v = u;
}
}
if (v == -1) break;
used[v] = true;
res += d[v];
for (int u = 0; u < n; u++) {
d[u] = min(d[u], G[v][u]); // 更新最小距离
}
}
cout << res << endl; // 输出生成树的总权值
return 0;
}
typedef pair<int, int> P; // 存储边权和终点的对
vector<P> G[MAX_V]; // 邻接表
bool used[MAX_V]; // 记录顶点是否已经加入生成树
int d[MAX_V]; // 记录生成树中与顶点最小边的权值
// Prim 算法求解最小生成树
long long Prim() {
long long res = 0; // 最小生成树的边权之和
priority_queue<P, vector<P>, greater<P>> que; // 存储以 d[i] 为关键字的优先队列
fill(d, d + V, INF);
d[0] = 0;
que.push(P(0, 0));
while (!que.empty()) {
P p = que.top();
que.pop();
int v = p.second;
if (used[v]) continue;
used[v] = true;
res += p.first;
for (int i = 0; i < G[v].size(); i++) {
int u = G[v][i].first;
int w = G[v][i].second;
if (d[u] > w) {
d[u] = w;
que.push(P(d[u], u));
}
}
}
return res;
}
int main() {
int V, E; // V 为顶点数,E 为边数
cin >> V >> E;
// 读入边,构建邻接表
for (int i = 0; i < E; i++) {
int u, v, w;
cin >> u >> v >> w;
G[u].push_back(P(v, w));
G[v].push_back(P(u, w));
}
long long res = Prim();
cout << res << endl; // 输出生成树的总权值
return 0;
}
最小生成树算法是图论中非常重要的一个问题,可以用于最优化设计和路由问题等应用领域。在实际应用中,可以灵活选择 Prim 或 Kruskal 算法来对不同类型的图结构进行求解,进一步优化算法效率。