引入
一、什么是图的最小生成树(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 }