一、Prim
1、描述
MST(Minimum Spanning Tree,最小生成树)问题有两种通用的解法,Prim算法就是其中之一,它是从点的方面考虑构建一颗MST,大致思想是:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。因为有N个顶点,所以该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。
2、说明:
初始状态:
设置2个数据结构:
lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说明以i为终点的边的最小权值=0,也就是表示i点加入了MST
mst[i]:表示对应lowcost[i]的起点,即说明边
我们假设V1是起始点,进行初始化(*代表无限大,即无通路):
lowcost[2]=6,lowcost[3]=1,lowcost[4]=5,lowcost[5]=*,lowcost[6]=*
mst[2]=1,mst[3]=1,mst[4]=1,mst[5]=1,mst[6]=1,(所有点默认起点是V1)
明显看出,以V3为终点的边的权值最小=1,所以边
此时,因为点V3的加入,需要更新lowcost数组和mst数组:
lowcost[2]=5,lowcost[3]=0,lowcost[4]=5,lowcost[5]=6,lowcost[6]=4
mst[2]=3,mst[3]=0,mst[4]=1,mst[5]=3,mst[6]=3
明显看出,以V6为终点的边的权值最小=4,所以边
此时,因为点V6的加入,需要更新lowcost数组和mst数组:
lowcost[2]=5,lowcost[3]=0,lowcost[4]=2,lowcost[5]=6,lowcost[6]=0
mst[2]=3,mst[3]=0,mst[4]=6,mst[5]=3,mst[6]=0
明显看出,以V4为终点的边的权值最小=2,所以边
此时,因为点V4的加入,需要更新lowcost数组和mst数组:
lowcost[2]=5,lowcost[3]=0,lowcost[4]=0,lowcost[5]=6,lowcost[6]=0
mst[2]=3,mst[3]=0,mst[4]=0,mst[5]=3,mst[6]=0
明显看出,以V2为终点的边的权值最小=5,所以边
此时,因为点V2的加入,需要更新lowcost数组和mst数组:
lowcost[2]=0,lowcost[3]=0,lowcost[4]=0,lowcost[5]=3,lowcost[6]=0
mst[2]=0,mst[3]=0,mst[4]=0,mst[5]=2,mst[6]=0
很明显,以V5为终点的边的权值最小=3,所以边
lowcost[2]=0,lowcost[3]=0,lowcost[4]=0,lowcost[5]=0,lowcost[6]=0
mst[2]=0,mst[3]=0,mst[4]=0,mst[5]=0,mst[6]=0
至此,MST构建成功,如图所示:
以上转载自https://blog.csdn.net/yeruby/article/details/38615045
接下来来看看代码吧。
1 #include2 using namespace std; 3 int n,m,ans; 4 int pre[5001];//与这个节点连接的父节点 5 int sum[5001];//这个点到它的父节点的值 6 bool flag[5001];//判断有没有加入最小树 7 struct node{ 8 int v,w;//连接的节点与边权 9 node(){}; 10 node(int vv,int ww) 11 { 12 v=vv; 13 w=ww; 14 } 15 }; 16 vector mp[10001];//方便存图 17 int prim() 18 { 19 int last=1;//记录 20 pre[1]=0;//代表这是这棵树的父节点 21 flag[1]=true;//加入最小数 22 for(int i=1;i //要循环n-1次 23 { 24 for(int j=0;j //更新每次的已连接节点 25 { 26 int vv=mp[last][j].v; 27 int ww=mp[last][j].w; 28 if(ww //如果更短 29 { 30 pre[vv]=last;//更新父节点 31 sum[vv]=ww;//更新值 32 } 33 } 34 int min=0x7fffffff;//记录最短边 35 int minid=0;//记录这个最短边连接的点 36 for(int j=1;j<=n;j++) 37 { 38 if(sum[j] false)//寻找没进最小树且更小 39 { 40 min=sum[j];//替换 41 minid=j; 42 } 43 } 44 ans+=min;//加边权 45 flag[minid]=true;//加入最小树 46 last=minid;//记录这个点 47 } 48 return ans;//返回最小生成树的所耗总值 49 } 50 int main() 51 { 52 cin>>n>>m; 53 for(int i=1;i<=n;i++) 54 { 55 sum[i]=0x7fffffff; //初始化 56 } 57 for(int i=1;i<=m;i++) 58 { 59 int x,y,z; 60 cin>>x>>y>>z; 61 mp[x].push_back(node(y,z)); 62 mp[y].push_back(node(x,z));//记录边 63 } 64 cout<<prim(); 65 return 0; 66 }
二、Kruskal算法
以下转载自https://blog.csdn.net/weixin_44336954/article/details/91345907
1、描述
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。 。
在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得
的 w(T) 最小,则此 T 为 G 的最小生成树。
最小生成树其实是最小权重生成树的简称。
许多应用问题都是一个求无向连通图的最小生成树问题。例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同;另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。
这一章主要介绍Kruskal算法。
Kruskal算法的时间复杂度:O(m*log(n))(点数n边数m)
2、主要思路
输入之后对边权值进行排序,然后按边权值从小到大进行合并(merge)操作,如果操作成功(被合并的两个点不在一棵树上),则把这两个顶点的边权值加入总数,最后输出total即可。
3、主要使用:
“并查集。”
首先把get和merge函数写好,为了方便,我把merge写成了bool类型:如果成功合并(要求合并的两个顶点不在一棵树上)就返回true。
然后是最正常的运用结构体进行循环读入,读入完成之后写cmp排序函数按边权值从小到大进行排序。
接下来才和并查集扯上关系,所以要重新定义fa数组,然后进行初始化;
接下来来看看代码吧。
1 #include2 using namespace std; 3 int n,m,ans; 4 int fa[10001]; 5 struct node{ 6 int v1,v2,w;//两个点和边权 7 node(){}; 8 node(int vv1,int vv2,int ww) 9 { 10 v1=vv1; 11 v2=vv2; 12 w=ww; 13 } 14 }mp[200001]; 15 bool sj(node x,node y)//比较函数 16 { 17 return x.w<y.w ; 18 } 19 int get(int x)//找祖先 20 { 21 if(fa[x]==x)return x; 22 else return fa[x]=get(fa[x]); 23 } 24 int Kruskal() 25 { 26 int num=0; 27 for(int i=1;i<=m;i++) 28 { 29 int x=mp[i].v1; 30 int y=mp[i].v2; 31 if(get(x)!=get(y))//如果这两个点不在同一集合,我们就把他们合在一起(加入最小树) 32 { 33 ans+=mp[i].w;//加上这个边权 34 fa[get(x)]=get(y);//合并 35 num++;//边数加加 36 if(num==n-1)break;//最小生成树边为点-1 37 } 38 } 39 return ans; 40 } 41 int main() 42 { 43 cin>>n>>m; 44 for(int i=1;i<=n;i++) 45 { 46 fa[i]=i;//每个祖先是自己 47 } 48 for(int i=1;i<=m;i++) 49 { 50 int x,y,z; 51 cin>>x>>y>>z; 52 mp[i].v1=x; 53 mp[i].v2=y; 54 mp[i].w=z;//存图 55 } 56 sort(mp+1,mp+1+m,sj);//排序 57 cout<<Kruskal(); 58 return 0; 59 }