轉自http://www.cnblogs.com/Veegin/archive/2011/04/29/2032388.html
今天從志權師兄那裏學會了最小生成樹。所謂生成樹,就是n個點之間連成n-1條邊的圖形。而最小生成樹,就是權值(兩點間直線的值)之和的最小值。
首先,要用二維數組記錄點和權值。如上圖所示無向圖:
int map[7][7];
map[1][2]=map[2][1]=4;
map[1][3]=map[3][1]=2;
......
然後再求最小生成樹。具體方法是:
1.先選取一個點作起始點,然後選擇它鄰近的權值最小的點(如果有多個與其相連的相同最小權值的點,隨便選取一個)。
如1作爲起點。
visited[1]=1;//標記點1,表明頂點1已加入最小生成樹
pos=1;
//用low[]數組不斷刷新最小權值,low[i](0<i<=點數)的值爲:i點到鄰近點(未被標記)的最小距離。
low[1]=0; //起始點i到鄰近點的最小距離爲0
low[2]=map[pos][2]=4;
low[3]=map[pos][3]=2;
low[4]==map[pos][4]=3;
low[5]=map[pos][5]=MaxInt; //無法直達
low[6]=map[pos][6]=MaxInt;
2.再在伸延的點找與它鄰近的兩者權值最小的點。
//low[]以3作當前位置進行更新
visited[3]=1;//標記點3
pos=3;
low[1]=0; //已標記,不更新
low[2]=map[1][2]=4; //比5小,不更新
low[3]=2; //已標記,不更新
low[4]=map[1][4]=3; //比1大,更新後爲:low[4]=map[3][4]=1;
low[5]=map[1][5]=MaxInt;//無法直達,不更新
low[6]=map[1][6]=MaxInt;//比2大,更新後爲:low[6]=map[3][6]=2;
3.如此類推...
所有權值相加就是最小生成樹,其值爲2+1+2+4+3=12。
至於具體代碼如何實現,現在結合POJ1258例題解釋。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
#include
<stdio.h> #include
<string.h> #define
MaxInt 65535 #define
N 110 //創建map二維數組儲存圖表,low數組記錄每2個點間最小權值,visited數組標記某點是否已訪問 int map[N][N],low[N],visited[N]; int n; int prim() { int i,j,pos,min,result=0; memset (visited,0, sizeof (visited)); //從某點開始,分別標記和記錄該點 visited[1]=1;pos=1; //第一次給low數組賦值 for (i=1;i<=n;i++) if (i!=pos)
low[i]=map[pos][i]; //再運行n-1次 for (i=1;i<n;i++) { //找出最小權值並記錄位置 min=MaxInt; for (j=1;j<=n;j++) if (visited[j]==0&&min>low[j])//如果還沒有訪問過,並且權值小於最小權值 { min=low[j];
pos=j; } //最小權值累加 result+=min; //標記該點 visited[pos]=1; //更新權值 for (j=1;j<=n;j++) if (visited[j]==0&&low[j]>map[pos][j]) low[j]=map[pos][j]; } return result; } int main() { int i,v,j,ans; while ( scanf ( "%d" ,&n)!=EOF) { //所有權值初始化爲最大 memset (map,MaxInt, sizeof (map)); for (i=1;i<=n;i++) for (j=1;j<=n;j++) { scanf ( "%d" ,&v); map[i][j]=map[i][j]=v; } ans=prim(); printf ( "%d\n" ,ans); } return 0; } |
在這裏感激志權師兄的教導!