ALDS1_12_A:Minimun Spanning Tree
阿新 • • 發佈:2018-11-26
ALDS1_12_A:Minimun Spanning Tree
題目:
https://cn.vjudge.net/problem/Aizu-ALDS1_12_A
程式碼如下:
#include <bits/stdc++.h> using namespace std; #define MAX 105 #define NIL (1 << 21) #define WHITE 0 #define BLACK 1 int n; int a[MAX][MAX]; //prime演算法的實現 int prime() { int color[MAX],d[MAX],p[MAX],mina,u,sum; for(int i = 0;i < n;i++) { color[i] = WHITE; d[i] = NIL; } d[0] = 0;//初始的時候,與第一個結點連線的權值為0 while(1) { mina = NIL; u = -1; for(int i = 0;i < n;i++) { if(mina > d[i] && color[i] != BLACK) //超出權值最小的那條邊,並且將已納入的點除外 { u = i; mina = d[i]; } } if(u == -1) break;//當所有點都為黑的時候跳出 color[u] = BLACK;//將連線後的點變黑 for(int v = 0;v < n;v++) { if(a[u][v] != -1 && color[v] != BLACK) //排除形成環 { if(d[v] > a[u][v]) { d[v] = a[u][v]; p[v] = u;//標記連線點的起始位置 } } } } sum = 0; for(int i = 1;i < n;i++) sum += a[p[i]][i]; return sum; } int main() { scanf("%d",&n); for(int i = 0;i < n;i++) { for(int j = 0;j < n;j++) scanf("%d",&a[i][j]); } cout << prime() << endl; return 0; }
這道題是要求最小生成樹,運用的是prime演算法,先來簡單介紹一下prime演算法。
prime演算法簡述:
prime演算法在求最小生成數的時候和圖中的邊數無關,只和圖中的頂點數相關,所以prime演算法適合求稠密圖的最小生成樹,演算法的複雜度為o(n * n)。
prime演算法大概步驟:
- 首先將圖中所有的定點都放入集合s,再定義一個集合t,此時集合t的元素為0個,也就是一個空集合。
- 然後將s集合中的某一個定點拿出並放入t集合中並在s集合中將其刪除,隨後記錄這個點在s集合中與其他連線點的權值,並且選出最短那個權值儲存下來,然後把那個連線點也放入t集合並在s集合中將其刪除,再儲存與這個點相連的權值,並且結合上一步的權值,找出最小權值所連線的那個點…隨後一直重複這個步驟,直到集合s集合為空。
- 集合t就是由Prime演算法得到的最小生成樹的結點,依照步驟2的結點連線這些頂點,得到的就是這個圖的最小生成樹。
需要注意的是每次把一個點放入集合t之後,那麼這個點在之後的連線中就不能再次出現了,否則就可能形成環,而最小生成樹是沒有環的。
演算法實現的具體過程:
1.首先定義一個colour陣列,先將圖中所有的結點都初始為“WHITE”(white代表的就是此結點沒有被放入集合t中),並且定義一個d[]陣列,d[]陣列用於記錄連線集合t內頂點與集合s內頂點的邊中權值最小的邊的權值。最後定義一個p[]陣列,這個陣列用於記錄集合t中頂點的父結點。
2.開始時先把u初始化為-1,d[0] = 0。通過遍歷找出最小權值的邊,並且連線這條邊的兩個頂點都沒有放入到t集合中,用變數u記錄這條邊的末位置。
3.隨後從u這個末位置出發尋找與其相連線的其他邊的權值,此時應該邊遍歷邊更新d[i]的值(永遠選擇到達那個頂點權值最小的那個),並且記錄p[i]。
4.重複上述2,3步驟,如果2中遍歷一遍之後u的值依然為-1,代表所有的點都已經放入集合t中了,則跳出迴圈。
5.最後累加每個點線連線線的權值(此時p[i]就可起到作用)。