Prim 算法是一种贪心算法(适合稠密图)
设G=(V, E) 是无向连通带权图,V={0, 1, 2, ..., n-1};设最小生成树 T=(U, E'),算法结束时 U=V,E'⊆E。
贪心选择的思想是:每次对于还未加入到 U 中(即 V-U 中)所有点顶,从这些顶点中选择一个顶点,选择的标准是,它距离 U 最近(和 U 中某个顶点的权值最小),将这个顶点加入到 U 中,并将这条边加入到 E' 中。当所有顶点都加入到了 U 中,算法结束。
C++代码:
#include <iostream>
#include <cassert>
#include <limits>
using namespace std;
/*
* n : 图的顶点个数
* u0:开始顶点
* C :带权邻接矩阵
*/
int prim(int n, int u0, int **C) {
assert(n > 0 && u0 >= 0 && u0 < n);
bool s[n];// 用于标记顶点是否加入到了 U 中
int closest[n];// closest[i],表示 V-U 中的一个顶点 i,在 U 中的最近邻接点 closest[i]
int lowcost[n];// lowcost[i],表示 V-U 中的一个顶点 i,到 U 中所有顶点的最短边的权值,即边 <i, closest[i]> 的权值
s[u0] = true;// 初始点默认已加入到 U 中
for(int i=0; i < n; i++) {
if(i != u0) {
s[i] = false;// 初始化:其他所有的点都未加入到 U 中,都在 V-U 中
closest[i] = u0;// 初始化:其他所有点,在 U 中的最近邻近点,都是初始点
lowcost[i] = C[u0][i];// 因此,它们对应的和 U 中的最短边的权值即使与初始顶点的权值
}
}
const int INFINITY = numeric_limits<int>::max();
for(int i=0; i < n; i++) {// 在 V-U 中寻找距离 U 最近的顶点并更新 closest 和 lowcost
int minWeight = INFINITY;
int t = u0;
for(int j=0; j < n; j++) {// 在 V-U 中寻找距离 U 最近的顶点 t
if(!s[j] && lowcost[j]<minWeight) {
t = j;
minWeight = lowcost[j];
}
}
if(t == u0)// 因为 u0 在 U 中,如果在 V-U 中找到了,t 不可能为 u0,为 u0 表示没有找到
break;// 说明在 V-U 中没有顶点和 U 中的任何一个顶点相邻了,没有必要再在 V-U 中继续浪费时间了
s[t] = true;// 找到这个点后,将其加入到 U 中
for(int j=0; j < n; j++) {// 因为 U 中加入了新的顶点,可能需要更新 V-U 中所有点顶的 closest 和 lowcost
if(!s[j] && (C[t][j]<lowcost[j])) {
lowcost[j] = C[t][j];
closest[j] = t;
}
}
}
int cost = 0;
for(int i=0; i < n; i++) {
if(i != u0)
cost += lowcost[i];
}
return cost;
}
int main() {
int N;
cin >> N;
int **C = new int*[N];
int minEdge = 0;
int u0 = 0;
for(int i=0; i < N; i++) {
C[i] = new int[N];
for(int j=0; j < N; j++) {
cin >> C[i][j];
if(minEdge > C[i][j]) {
minEdge = C[i][j];
u0 = i;
}
}
}
cout << prim(N, u0, C) << endl;
return 0;
}