問題提出:
一個公司計劃建立一個通信網絡來連接它的一個計算機中心。可以用租用的電話線連接這些中心的任何一對。應當妊娠瘙癢哪些連接,以便保證在任何兩個計算機中心之間都有通路,且網絡的總成本最小?可以用下較長所示的帶權圖爲這個問題建模,其中頂點表示計算機中心,邊表示可能租用的電話線,邊上的權是邊所表示的電話線的月租費。通過找出一棵生成樹,使得這棵樹的各邊的權之和爲最小,就可以解決這個問題。這樣的生成樹稱爲最小生成樹。
最小生成樹定義:
在一個具有V個節點的連通無向圖中,找到一個子圖,該子圖包含原圖的所有節點和部分連接邊,且不能形成迴路,同時子圖邊的權值總和最小。
最小生成樹的算法:
根據對安全邊的不同規則,有兩種算法可以生成最小生成樹。即Kruskal算法和Prim算法。
1.普林算法(Prim)
設是帶權值的連通圖,A是上最小生成樹中邊的集合:
(1)初始令,(), A=NULL
(2)在所有,的邊中,找一條權值最小的邊
(3)將併入集合A,同時併入
(4)重複上述操作直至爲止,則爲的最小生成樹
判斷規則:在所有,的邊中,找一條權值最小的邊,連接該邊,但不能形成迴路;
舉例:
該圖爲原始圖
第一步:選取一個初始節點a爲根節點,並找到權值爲最小的邊,即爲ab;
第二步:在所有,的邊中,找一條權值最小的邊;即權值最小邊爲bc或者ah;這裏選取bc
第三步:在所有,的邊中,找一條權值最小的邊;即選擇ci;
第四步:在所有,的邊中,找一條權值最小的邊;即選擇cf;
第五步:在所有,的邊中,找一條權值最小的邊;即選擇fg;
第六步:在所有,的邊中,找一條權值最小的邊;即選擇gh;
第七步:在所有,的邊中,找一條權值最小的邊;必須保證不能形成迴路,即選擇cd;
最後一步:在所有,的邊中,找一條權值最小的邊;必須保證不能形成迴路,即選擇de;由於樹包含了所有節點,且滿足,則在此終止,爲的最小生成樹。
//d爲圖的鄰近矩陣,p爲所生成樹的鄰近矩陣,
//n爲圖中點的個數
void Prim(int **d,int **p,int n)
{
int i,j,tmp,iT,iL;
vector<int> T,L;//T是要生成的樹,L爲還沒生成樹的頂點
for(i=0;i<n;i++)
L.push_back(i);//給L賦初始
while(L.size()){//當L爲空時,即已經生成樹完畢
tmp=d[0][0];iT=iL=0;//以第一個點作爲初始點
for(i=0;i<T.size();i++){//找出樹與非樹頂點之間距離最小的點,並記錄
for(j=L.size()-1;j>=0;j--){
if(tmp>=d[T[i]][L[j]]){
tmp=d[T[i]][L[j]];
iT=i;iL=j;
}
}
}
cout<<L[iL]<<" ";
T.push_back(L[iL]);//將找到的點添加到樹中
p[T[iT]][L[iL]]=1;
L.erase(L.begin()+iL);//且從剩餘的點中刪除
}
cout<<endl;
}