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;
}