用Prim算法求解最小生成树:代码实现与分析
在图论的世界里,最小生成树(MST)问题是一个经典且重要的课题。今天,我们就来深入探讨使用Prim算法解决最小生成树问题,并对一段C++代码进行详细解读。
一、Prim算法简介
Prim算法是一种贪心算法,用于在加权无向连通图中找到一棵最小生成树。其核心思想是从图中的任意一个顶点开始,逐步扩展生成树,每次选择与当前生成树相连的边中权重最小的边,将对应的顶点加入生成树,直到所有顶点都被包含在生成树中。
二、代码解读
下面是实现Prim算法的C++代码:
#include<iostream>
#define int long long
#define N 10005
using namespace std;
int g[N][N];
int w[N], pr[N];
int n,m;
int sum;
void prime(){
int j, mmin = 0x3f3f3f3f;
for (int i = 2; i <= n; i++){
w[i] = g[1][i];
pr[i] = 0;
}
pr[1]=-1;
for (int i = 2; i <= n; i++){
mmin = 0x3f3f3f3f;
int k = 0;
for (int j = 1; j <= n; j++){
if (pr[j]!=-1&&w[j] <= mmin){
mmin = w[j];
k = j;
}
}
pr[k] = -1;
for (int j = 1; j <= n; j++)
{
if (pr[j]!=-1&&w[j]>=g[k][j])
{
w[j] = g[k][j];
pr[j] = k;
}
}
}
}
signed main() {
cin >> n >> m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
g[i][j]=0x3f3f3f3f;
}
while (m--){
int x, y, c;
cin >> x >> y >> c;
if (c < g[x][y]) {
g[x][y] = c;
g[y][x] = c;
}
}
prime();
for (int i = 1; i <= n; i++){
sum+=w[i];
}
if(sum>=0x3f3f3f3f/2){
cout<<"impossible";
return 0;
}
cout << sum << endl;
return 0;
}
1. 变量定义
g[N][N]
:二维数组,用于存储图的邻接矩阵,g[i][j]
表示顶点i
和顶点j
之间的边的权重。w[N]
:数组,用于记录每个顶点到当前生成树的最小距离。pr[N]
:数组,用于记录每个顶点在最小生成树中的前驱节点。n
:表示图中顶点的数量。m
:表示图中边的数量。sum
:用于累加最小生成树的边的权重和。
2. prime
函数
- 初始化部分:
for (int i = 2; i <= n; i++){
w[i] = g[1][i];
pr[i] = 0;
}
pr[1]=-1;
这部分代码从顶点2开始,将每个顶点到顶点1的距离初始化为g[1][i]
,并将顶点1的前驱节点设为-1,表示它是起始节点。
- 核心循环部分:
for (int i = 2; i <= n; i++){
mmin = 0x3f3f3f3f;
int k = 0;
for (int j = 1; j <= n; j++){
if (pr[j]!=-1&&w[j] <= mmin){
mmin = w[j];
k = j;
}
}
pr[k] = -1;
for (int j = 1; j <= n; j++)
{
if (pr[j]!=-1&&w[j]>=g[k][j])
{
w[j] = g[k][j];
pr[j] = k;
}
}
}
外层循环从顶点2开始,每次找到距离当前生成树最近的顶点k
。内层第一个循环遍历所有顶点,找到未被加入生成树(pr[j]!=-1
)且距离最小的顶点k
。然后将顶点k
的前驱节点设为-1,表示它已被加入生成树。内层第二个循环更新其他未加入生成树的顶点到当前生成树的距离和前驱节点。
3. main
函数
- 初始化邻接矩阵:
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
g[i][j]=0x3f3f3f3f;
}
将邻接矩阵的所有元素初始化为一个很大的值0x3f3f3f3f
,表示初始时顶点之间没有边相连。
- 读入边的信息:
while (m--){
int x, y, c;
cin >> x >> y >> c;
if (c < g[x][y]) {
g[x][y] = c;
g[y][x] = c;
}
}
读入图的边的信息,包括两个顶点x
和y
以及边的权重c
。如果读入的边的权重小于当前邻接矩阵中对应位置的值,则更新邻接矩阵。
- 调用
prime
函数并计算结果:
prime();
for (int i = 1; i <= n; i++){
sum+=w[i];
}
if(sum>=0x3f3f3f3f/2){
cout<<"impossible";
return 0;
}
cout << sum << endl;
调用prime
函数计算最小生成树,然后累加w
数组中的值得到最小生成树的权重和sum
。如果sum
大于等于一个很大的值的一半(这里用0x3f3f3f3f/2
判断),说明图不连通,无法计算最小生成树,输出impossible
;否则输出最小生成树的权重和。
三、总结
这段代码通过Prim算法实现了求解最小生成树的功能。在理解和编写这类代码时,关键在于清晰地把握Prim算法的核心思想,即如何逐步扩展生成树,以及如何正确地更新顶点到生成树的距离和前驱节点。同时,对图的初始化、边的读入和结果的判断与输出等细节也需要仔细处理。希望通过这篇博客,能帮助大家更好地理解Prim算法及其在C++中的实现。