【模板】最小生成树之Prim

引入

   一、什么是图的最小生成树(MST)?

 不知道大家还记不记得树的一个定理:N个点用N-1条边连接成一个连通块,形成的图形只可能是树,没有别的可能。

  一个有N个点的图,边一定是大于等于N-1条的。图的最小生成树,就是在这些边中选择N-1条出来,连接所有的N个点。这N-1条边的边权之和是所有方案中最小的。

二、最小生成树用来解决什么问题?

就是用来解决如何用最小的“代价”用N-1条边连接N个点的问题。

Prim算法

  Prim算法采用与Dijkstra、Bellman-Ford算法一样的“蓝白点”思想:白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点。

算法描述:

以1为起点生成最小生成树,min[v]表示蓝点v与白点相连的最小边权。MST表示最小生成树的权值之和。

a)初始化:min[v]= ∞(v≠1); min[1]=0;MST=0;

b)for (i = 1; i<= n; i++)

  1.寻找min[u]最小的蓝点u。

  2.将u标记为白点

  3.MST+=min[u]

  4.for 与白点u相连的所有蓝点v if (w[u][v]<min[v]) min[v]=w[u][v];

c)算法结束: MST即为最小生成树的权值之和

算法分析&思想讲解:

Prim算法每次循环都将一个蓝点u变为白点,并且此蓝点u与白点相连的最小边权min[u]还是当前所有蓝点中最小的。这样相当于向生成树中添加了n-1次最小的边,最后得到的一定是最小生成树。

我们通过对下图最小生成树的求解模拟来理解上面的思想。

蓝点和虚线代表未进入最小生成树的点、边;白点和实线代表已进入最小生成树的点、边。

初始时所有点都是蓝点,min[1]=0,min[2、3、4、5]=∞。权值之和MST=0。

第一次循环自然是找到min[1]=0最小的蓝点1。将1变为白点,接着枚举与1相连的所有蓝点2、3、4,修改它们与白点相连的最小边权。

min[2]=w[1][2]=2;

min[3]=w[1][3]=4;

min[4]=w[1][4]=7;

第二次循环是找到min[2]最小的蓝点2。将2变为白点,接着枚举与2相连的所有蓝点3、5,修改它们与白点相连的最小边权。

min[3]=w[2][3]=1;

min[5]=w[2][5]=2;

第三次循环是找到min[3]最小的蓝点3。将3变为白点,接着枚举与3相连的所有蓝点4、5,修改它们与白点相连的最小边权。

min[4]=w[3][4]=1;

由于min[5]=2 < w[3][5]=6;

所以不修改min[5]的值。

最后两轮循环将点4、5以及边w[2][5],w[3][4]添加进最小生成树。

最后权值之和MST=6。

这n次循环,每次循环我们都能让一个新的点加入生成树,n次循环就能把所有点囊括到其中;每次循环我们都能让一条新的边加入生成树,n-1次循环就能生成一棵含有n个点的树;每次循环我们都取一条最小的边加入生成树,n-1次循环结束后,我们得到的就是一棵最小的生成树。

这就是Prim采取贪心法生成一棵最小生成树的原理。 算法时间复杂度:O($N^2$)。

【Prim参考代码】

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int g[101][101];              //邻接矩阵
 4 int minn[101];                //minn[i]存放蓝点i与白点相连的最小边权
 5 bool u[101];                  //u[i]=True,表示顶点i还未加入到生成树中;u[i]=False,表示顶点i已加入到生成树中 
 6 int n,ans;
 7 int main()
 8 {
 9     scanf("%d",&n);
10     for(int i=1;i<=n;i++)
11         for(int j=1;j<=n;j++) 
12             scanf("%d",&g[i][j]);  
13     memset(minn,0x7f,sizeof(minn));   //初始化为maxint
14     minn[1]=0;
15     memset(u,1,sizeof(u));            //初始化为True,表示所有顶点为蓝点
16     for(int i=1;i<=n;i++){
17         int k=0;
18         for(int j=1;j<=n;j++) //找一个与白点相连的权值最小的蓝点k
19             if(u[j]&&(minn[j]<minn[k])) k=j;
20         u[k]=0;                    //蓝点k加入生成树,标记为白点
21         for(int j=1;j<=n;j++)         //修改与k相连的所有蓝点
22             if(u[j]&&(g[k][j]<minn[j])) minn[j]=g[k][j];
23     }
24     for(int i=1;i<=n;i++)
25         ans+=minn[i];//累加权值 
26     cout<<ans<<endl;
27     return 0;
28 }

猜你喜欢

转载自www.cnblogs.com/maoyiting/p/11821158.html