最小生成树之普里姆(Prim)算法

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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章