求最小生成樹算法——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集合)變量
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 }