最小生成樹

http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2144php

轉自http://www.cnblogs.com/pony1993/archive/2012/07/17/2595237.htmlhtml

克魯斯卡爾(Kruskal)算法(只與邊相關)node

 

算法描述:克魯斯卡爾算法須要對圖的邊進行訪問,因此克魯斯卡爾算法的時間複雜度只和邊又關係,能夠證實其時間複雜度爲O(eloge)。算法

算法過程:ide

1.將圖各邊按照權值進行排序spa

2.將圖遍歷一次,找出權值最小的邊,(條件:這次找出的邊不能和已加入最小生成樹集合的邊構成環),若符合條件,則加入最小生成樹的集合中。不符合條件則繼續遍歷圖,尋找下一個最小權值的邊。htm

3.遞歸重複步驟1,直到找出n-1條邊爲止(設圖有n個結點,則最小生成樹的邊數應爲n-1條),算法結束。獲得的就是此圖的最小生成樹。blog

 

克魯斯卡爾(Kruskal)算法由於只與邊相關,則適合求稀疏圖的最小生成樹。而prime算法由於只與頂點有關,因此適合求稠密圖的最小生成樹。排序

並查集 +快排遞歸

代碼:

View Code
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include <algorithm>
 4 typedef struct node
 5 {
 6     int x, y, z;
 7 }st;
 8 st q[10001];
 9 using namespace std;
10 bool cmp(const st &a,const st &b)
11 {
12     return a.z<b.z;
13 }
14 int num,father[201],sum;
15 int find(int a)
16 {
17     if(a!=father[a])
18         father[a] = find(father[a]);
19     return father[a];
20 }
21 void union1(int a, int b,int c)
22 {
23     a = find(a);
24     b = find(b);
25     if(a!=b)
26     {
27         father[a] = b;
28         sum+=c;
29         num++;
30     }
31 }
32 int main()
33 {
34     int i, j, n, m;
35     while(scanf("%d%d", &m, &n)!=EOF)
36     {
37         num = 0;
38         sum = 0;
39         for(i = 0 ; i <= m ; i++)
40             father[i] = i;
41         for(i = 0 ; i < n ; i++)
42             scanf("%d%d%d", &q[i].x,&q[i].y,&q[i].z);
43         sort(q,q+n,cmp);
44         for(i = 0 ; i < n&&num<m-1 ; i++)
45             union1(q[i].x,q[i].y,q[i].z);
46         printf("%d\n",sum);
47     }
48     return 0;
49 }  

普利姆(Prime)算法(只與頂點相關)

 

算法描述:

普利姆算法求最小生成樹時候,和邊數無關,只和定點的數量相關,因此適合求稠密網的最小生成樹,時間複雜度爲O(n*n)。

算法過程:

1.將一個圖的頂點分爲兩部分,一部分是最小生成樹中的結點(A集合),另外一部分是未處理的結點(B集合)。

2.首先選擇一個結點,將這個結點加入A中,而後,對集合A中的頂點遍歷,找出A中頂點關聯的邊權值最小的那個(設爲v),將此頂點從B中刪除,加入集合A中。

3.遞歸重複步驟2,直到B集合中的結點爲空,結束此過程。

4.A集合中的結點就是由Prime算法獲得的最小生成樹的結點,依照步驟2的結點鏈接這些頂點,獲得的就是這個圖的最小生成樹。

 

算法實現具體過程:

 

1.將第一個點放入最小生成樹的集合中(標記visit[i]=1意思就是最小生成樹集合)。

2.從第二個點開始,初始化lowcost[i]爲跟1點相連(僅僅相連)的邊的權值(lowcost[i]不是這個點的最小權值!在之後會逐步更新)。

3.找最小權值的邊。

從第二點開始遍歷,若是不是最小生成樹的集合的點,則找出從2到n的最小權值(lowcost[j])。

4.將找出來的最小權值的邊的頂點加入最小生成樹的集合中(標記visit[i] = 1),權值相加。

5.更新lowcost[j]集合。

假設第一次:lowcost[2]表明與1相連的點的權值,如今加入了k點。則比較k點與2點的邊map[k][2]和lowcost[2]的大小,若lowcost[2]大,則lowcost[2] = map[k][2]。(關鍵步驟:實質就是每在最小生成樹集合中加入一個點就須要把這個點與集合外的點比較,不斷的尋找兩個集合之間最小的邊)

6.循環上述步驟,直到將所有頂點加入到最小生成樹集合爲止。

代碼:

View Code
 1 #include<stdio.h>
 2 #include<string.h>
 3 #define INF 0x3f3f3f3f
 4 int w[101][101];
 5 int visit[101],low[101],sum;
 6 void Prime(int n)
 7 {
 8     int i,j,k;
 9     memset(visit,0,sizeof(visit));//初始化
10     visit[1] = 1;
11     for(i = 2 ;i <= n ; i++)
12         low[i] = w[1][i];
13     for(i = 1 ; i <= n ; i++)//尋找權值最小的邊
14     {
15         int max = INF;
16         for(j = 1 ; j <= n ; j++)
17             if(!visit[j]&&max>low[j])//找到最小邊所連的節點 加入visit集合中
18                 max = low[k=j];
19         if(max == INF)
20             break;
21         visit[k] = 1;
22         sum+=max;
23         for(j = 1 ; j <= n ; j++)
24             if(!visit[j]&&low[j]>w[k][j])//更新low
25             low[j] = w[k][j];
26     }
27 }
28 int main()
29 {
30     int n,m,i,j,v1,v2,e;
31     while(scanf("%d%d", &n,&m)!=EOF)
32     {
33         memset(w,INF,sizeof(w));
34         sum = 0;
35         for(i = 1 ;i <= m ; i++)
36         {
37             scanf("%d%d%d", &v1,&v2,&e);
38             if(w[v1][v2]>e)
39             {
40                 w[v1][v2] = e;
41                 w[v2][v1] = e;
42             }
43         }
44         Prime(n);
45         printf("%d\n",sum);
46     }
47     return 0;
48 }
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息