1. 程式人生 > >Prim算法解決最小生成樹

Prim算法解決最小生成樹

tor 一個 play spl 連通圖 輸入 rim 網絡 amp

一、最小生成樹問題

什麽是最小生成樹問題?給你一個帶權連通圖,需要你刪去一些邊,使它成為一顆權值最小的樹。

二、Prim算法

1)輸入:輸入一個帶權連通圖,頂點集合V,邊集合E

2)初始化:Vnew={x},x為任意一個頂點,作為起始點,Enew={},為空

3)在集合E中選擇權值最小的邊<u,v>,其中u為集合Vnew中的頂點,而v不在集合Vnew中但在V中,(若有多條滿足條件且權值相同時,可任選其中一條)

4)將v收錄集合Vnew中,將<u,v>收錄Enew中

5)重復步驟4、5,直到所有的頂點都被收錄到Vnew中

6)輸出:輸出由Vnew和Enew所描述的最小生成樹

證明:

這就是一個樸素的貪心,我們只需證明這種貪心策略是正確的。先看一下這個性質:

MST性質:設G=(V,E)是一個連通網絡,U是頂點集V的一個真子集。若(u,v)是G中一條“一個端點在U中(例如:u∈U),另一個端點不在U中的邊(例如:v∈V-U),且(u,v)具有最小權值,則一定存在G的一棵最小生成樹包括此邊(u,v)。

假如通過其他途徑,已經得到一顆不包含(u,v)的最小生成樹,那麽把(u,v)加入到這棵生成樹,必定成環,由於在某點沒有選(u,v),則必定可以在環上找到一個權值不小於(u,v)的邊,刪去此邊,用(u,v)代替,仍是一顆最小生成樹且總權值更小。所以由反證法知,一定存在一棵最小生成樹包含(u,v)。所以可以通過每次選擇符合條件的權值最小邊,來得到其中一種最小生成樹。

三、算法實現

思路:這裏用鄰接矩陣A存儲圖(因為Prim適合稠密圖,時間復雜度一般為O(n^2)),用數組lowcost[MAXN]記錄未收錄頂點到Vnew的最小費用,用vis[MAXN]記錄是否已收錄。

1)任取一頂點s,收錄到Vnew中(這裏簡化為vis[s] = true)

2) 初始化,將lowcost[x]初始化為到s的費用

將下列步驟進行n-1次

3)找到最小的lowcost[x]

4)若沒有找到符合條件的最小lowcost[x],跳出循環

5)將x收錄到Vnew

6) 更新lowcost[ ] (因為新收錄的頂點可能影響lowcost)

7)若有頂點未被收錄,說明圖不連通

代碼:

技術分享圖片
#include<cstdio>
#include<stdbool.h>
#include<vector>
const int INF = 0x3f3f3f3f;
const int MAXN = 100 + 10;
bool vis[MAXN];
int lowcost[MAXN];
int cost[MAXN][MAXN];
//點是從0 ~ n-1
//耗費矩陣cost[][] 
int Prim(int cost[][MAXN], int s, int n)
{
    int ans = 0;
    memset(vis, false, sizeof(vis));
    vis[s] = true;
    for (int i = 0; i < n; i++)  lowcost[i] = cost[s][i];
    for (int i = 1; i < n; i++)
    {
        int minc = INF;
        int pos = -1;
        for (int j = 0; j < n; j++)
        {
            if (!vis[j] && lowcost[j] < minc)
            {
                minc = lowcost[j];
                pos = j;
            }
        }
        if (i < n - 1 && pos < 0)  return -1;
        vis[pos] = true;
        ans += minc;
        for (int j = 0; j < n; j++)
        {
            if (!vis[j] && cost[pos][j] < lowcost[j])
                lowcost[j] = cost[pos][j];
        }
    }
    return ans;
}
View Code

四、算法優化

據說可以用二叉堆、斐波那契堆等,先給自己挖一個坑,日後再補吧。

Prim算法解決最小生成樹