最小生成树Prim算法
求最小生成树算法——Prim
例题:
https://www.luogu.org/problem/P3366
算法:
时间复杂度 : $O(n^2)$
- 算法主体思想:
$\text{Prim}$ 算法主要是用到贪心的思想,假设我们有两个集合 $A$ 和 $B$,$A$ 集合表示最小生成树集合(及 $A$ 集合中的点都在最小生成树中),$B$ 集合表示非最小生成树集合(及 $B$ 集合中的点都不在最小生成树中)。一开始,我们可以随便将一个点放入集合 $A$,然后我们选择到最小生成树中距离最小的点放入 $A$ 集合(前提是最小 $\&$$\&$ 当前点在 $B$ 集合),然后用当前点更新其他在 $B$ 集合中的点到 $A$ 集合的距离。以此类推,循环 $n$ 次之后(一共有 $n$ 个点,所以 $n$ 次之后,$B$ 集合为空,所有点都在 $A$ 集合),就可得到最小生成树。
- 算法主要变量声明:
我们可以记录每个点到最小生成树集合中的最小距离,记为$dis_i$ —— 第 $i$ 个点到最小生成树集合中的最小距离(注意:是到最小生成树集合的最短距离,不是到起点)。然后我们还需知道一个点是否在最小生成树集合中,于是我们用 $vis_i$ 表示 $i$ 是否在最小生成树集合中(在 $A$ 集合,还是在 $B$ 集合)。当然我们可以用 $f_{i,j}$ 来记录第 $i$ 个点到第 $j$ 个点的距离。
- 算法主要步骤:
1. 初始化所有点都在 $B$ 集合中,所有点到 $A$ 集合中的距离都为$0$;
2. 随机选择一个点(选 $1$ 即可),并将当前点到最小生成树的距离为 $0$;
3. 重复 $n$ 次以下步骤(及把所有点都放到A集合)
- 定义变量 $minn$ 表示每次搜到的到最小生成树集合的最小距离(初始化为 $INF$),$k$ 表示搜到的最小值的编号
- 搜索 $B$ 集合中到 $A$ 集合的最小值
- 标记搜到的点在 $A$ 集合
- $ans$ 来累加当前点到最小生成树集合的距离
- 通过当前搜到的点 $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 void prim(int s) 6 { 7 memset(vis, 0, sizeof(vis)); 8 memset(dis, INF, sizeof(dis)); 9 dis[s] = 0; 10 for(int i = 1; i <= n; ++i) 11 { 12 int minn = INF;//用来记录每次搜到的离最小生成树集合的最小值 13 int k = 0;//用来记录搜到最小值的编号 14 for(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(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 int main() 39 { 40 memset(f, INF, sizeof(f)); 41 scanf("%d %d", &n, &m); 42 for(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 }

浙公网安备 33010602011771号