[OI学习笔记]最小生成树之Prim算法

背景

  今天遇到这样一道题:(洛谷2820

        某个局域网内有n(n<=100)台计算机,由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用f(i,j)表示i,j之间连接的畅通程度,f(i,j)值越小表示i,j之间连接越通畅,f(i,j)为0表示i,j之间无网线连接。

    输出Σf(i,j)的的最大值

    是最小生成树没错了,于是去温习了一遍图论。。。

    最小生成树定义(摘自百度百科):

         在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此的权重,若存在 T 为 E 的子集(即)且为无循环图,使得
w(T) 最小,则此 T 为 G 的最小生成树。
        
最小生成树其实是最小权重生成树的简称。 [1] 
      其实就是给你一个有n个点的无向联通图,每条边有一个选择的费用, 你需要只选择其中的n-1条边(因为n个点的联通图要保证无环最多只能有n-1条边),使得这个图仍然联通,保证不构成环,且费用和最小。
    以下面这张图为例:
        
 
    它的最小生成树当然是:
       
    实现最小生成树有两种算法,分别是Prim算法和Kruskal算法(因为还有蛋疼的开学考要准备,就先介绍一下Prim,过几天更Kruskal)
   

Prim算法 

    贪心策略:

        1)把已经在树中的点全部装进集合A,其余装进集合B,这样就保证不构成环
        2)每次选取连接A集合与B集合的边中的最短的一条进行连接,这样保证了边权和最小
    实现方法:
        1)随便找一个起始点(反正最后都在树上),并装进A集合
        2)循环n-1次,其中每次:
            1)选取连接A集合与B集合的边中的最短的一条进行连接
              2)将新加入的点加入A集合
        3)n-1次之后完成

    代码:(摘自模版题洛谷3366的题解)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define inf 0x7fffffff
 4 #define maxn 5005
 5 int cost[maxn][maxn],minn,n,m,v2[maxn],tot=1,now,ans;
 6 bool v1[maxn];
 7 inline void getcost()
 8 {
 9     scanf("%d%d",&n,&m);
10     for(int i=1;i<=n;i++)
11     {
12         for(int j=1;j<=n;j++)
13         {
14             cost[i][j]=inf;
15         }
16     }
17     //先将数组赋为极大值
18     for(int i=1,u,v,w;i<=m;i++)
19     {
20         scanf("%d%d%d",&u,&v,&w);
21         if(cost[u][v]>w)
22         {
23             cost[u][v]=cost[v][u]=w;
24         }
25     }
26     //初始化cost数组
27     for(int i=1;i<=n;i++)
28     {
29         v2[i]=cost[1][i];
30     }
31     v1[1]=1;
32     //找出与1节点相连的边并进行标记
33 }
34 inline int prim()
35 {
36     while(tot<n)
37     //最小生成树的概念,边数比点数小一
38     {
39         minn=inf; 
40         //附上初值
41         tot++;
42         //已经使用边数++
43         for(int i=1;i<=n;i++)
44         {
45             if(!v1[i]&&v2[i]<minn)
46             {
47                 minn=v2[i];
48                 now=i;
49             }
50         }
51         //找出与该点相连的最小边
52         ans+=minn;
53         //更新答案
54         for(int i=1;i<=n;i++)
55         {
56             if(v2[i]>cost[now][i]&&!v1[i])
57             {
58                 v2[i]=cost[now][i];
59             }
60         }
61         v1[now]=1;
62         //在找出与now节点相连的边并进行标记
63     }
64     return ans;
65 }
66 int main()
67 {
68     getcost();
69     printf("%d",prim());
70     return 0;
71 }

    再次以图一为例,

    输入:

4 5
1 2 40
1 3 20
1 4 30
2 3 30
4 3 10

     输出:

60

    完全正确。

本篇文章为SHINE_GEEK原创,转载请注明来源!
written_by:SHINE_GEEK

-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------
原文地址:https://www.cnblogs.com/sjrb/p/9524741.html