最小生成树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集合)

  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 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 }

 

posted @ 2019-07-20 13:49  louis_11  阅读(1052)  评论(0)    收藏  举报