最小生成樹Prim算法

求最小生成樹算法——Primc++


例題:算法

https://www.luogu.org/problem/P3366spa

算法:code

時間複雜度 : O(n²)blog

  • 算法主體思想:

prim算法主要是用到貪心的思想,假設咱們有兩個集合A和B,A集合表示最小生成樹集合(及A集合中的點都在最小生成樹中),B集合表示非最小生成樹集合(及B集合中的點都不在最小生成樹中)。一開始,咱們能夠隨便將一個點放入集合A,而後咱們選擇到最小生成樹中距離最小的點放入A集合(前提是最小 && 當前點在B集合),而後用當前點更新其餘在B集合中的點到A集合的距離。以此類推,循環n次以後(一共有n個點,因此n次以後,B集合爲空,全部點都在A集合),就可獲得最小生成樹。get

  • 算法主要變量聲明:

咱們能夠記錄每一個點到最小生成樹集合中的最小距離,記爲dis[i]————第i個點到最小生成樹集合中的最小距離(注意:是到最小生成樹集合的最短距離,不是到起點)。而後咱們還需知道一個點是否在最小生成樹集合中,因而咱們用vis[i]表示i是否在最小生成樹集合中(在A集合,仍是在B集合)。固然咱們能夠用f[i][j]來記錄第i個點到第j個點的距離。it

  • 算法主要步驟:

1. 初始化全部點都在B集合中,全部點到A集合中的距離都爲0模板

2. 隨機選擇一個點(選1便可),並將當前點到最小生成樹的距離爲0class

3. 重複n次如下步驟(及把全部點都放到A集合)變量

  1. 定義變量minn表示每次搜到的到最小生成樹集合的最小距離(初始化爲INF), k表示搜到的最小值的編號
  2. 搜索B集合中到A集合的最小值
  3. 標記搜到的點在A集合
  4. ans來累加當前點到最小生成樹集合的距離
  5. 經過當前搜到的點k,來更新其餘點到最小生成樹集合的最短距離

4. 獲得最小生成樹,邊權之和在ans裏


示意圖:


code:

 1 #include <bits/stdc++.h>
 2 #define INF 0x3f3f3f3f//定義最大值(0x3f3f3f3f是一個很大的數) 
 3 using namespace std;  4 int n, m, dis[1001], vis[1001], f[1001][1001], ans;//dis[i]表示第i個點到最小生成樹集合的最小值(及到最小生成樹中任意點的最小值), vis[i]表示第i個點是否在最小生成樹中, f[i][j]表示從第i個點到達第j個點的最小值(沒法到達就賦INF), ans記錄最小生成樹邊權 
 5 inline void prim(int s)//Prim模板 
 6 {  7     memset(vis, 0, sizeof(vis));//初始化爲未在最小生成樹中 
 8     memset(dis, INF, sizeof(dis));//初始化爲最大值 
 9     dis[s] = 0;//初始點s到最小生成樹生成樹中距離爲0 
10     for(register int i = 1; i <= n; ++i)//n個點都要連通就要把n個點都放進最小生成樹裏 
11  { 12         int minn = INF;//用來記錄每次搜到的離最小生成樹集合的最小值 
13         int k = 0;//用來記錄搜到最小值的編號 
14         for(register int j = 1; j <= n; ++j)//尋找最小值 
15  { 16             if(!vis[j] && dis[j] < minn)//不能訪問過 && 離最小生成樹的距離最短 
17  { 18                 minn = dis[j];//替換 
19                 k = j; 20  } 21  } 22         if(!k)//沒有點在最小生成樹中了(這個也能夠判斷圖是否連通,若是k沒有值,就表明圖沒有連通,由於最小生成樹中點的數量必定是n(生成樹的定義就是用n - 1條邊,使得n個點能互相到達)) 
23  { 24             break; 25  } 26         vis[k] = 1;//標記 
27         ans += dis[k];//累加最小值 
28         for(register int j = 1; j <= n; ++j) 29  { 30             if(!vis[j] && dis[j] > f[k][j])//更新長度(這裏是到最小生成樹集合的最短長度,不是到s的最短長度) 
31  { 32                 dis[j] = f[k][j];//更新 
33  } 34  } 35  } 36     return; 37 } 38 signed main() 39 { 40     memset(f, INF, sizeof(f));//賦最大值 
41     scanf("%d %d", &n, &m); 42     for(register int i = 1, x, y, z; i <= m; ++i) 43  { 44         scanf("%d %d %d", &x, &y, &z); 45         f[x][y] = f[y][x] = min(f[x][y], z)/*當心毒瘤數據*/;//連雙向邊 
46  } 47     prim(1);//從1開始就行了 
48     printf("%d", ans); 49     return 0; 50 }
相關文章
相關標籤/搜索