图论——最小生成树
最小生成树算法
是图论的一种算法
用N个点用N-1条边连成一棵树
所以在图中选N个点与N-1条边
使在所有方案中和最小
1°:
Prim算法
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew= {x},其中x为集合V中的任一节点(起始点),Enew= {},为空;
3).重复下列操作,直到Vnew= V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
这个算法你就当个笑话看看吧,反正也不常用
2°
Kruskal算法
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
然后上代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 5 using namespace std; 6 7 int f[300000]; 8 9 struct point{ 10 int x; 11 int y; 12 int z; 13 }edge[300000]; 14 15 bool cmp(point a,point b) 16 { 17 return a.z<b.z; 18 } 19 20 inline int Find(int a) 21 { 22 if(f[a]!=a) f[a]=Find(f[a]); 23 return f[a]; 24 } 25 26 inline void join(int a,int b) 27 { 28 int fa=Find(a); 29 int fb=Find(b); 30 if(fa!=fb) f[fa]=fb; 31 } 32 33 int main(void) 34 { 35 36 int n,m; 37 38 scanf("%d%d",&n,&m); 39 40 for(register int i=1;i<=m;i++) 41 scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z); 42 43 sort(edge+1,edge+1+m,cmp); 44 45 for(register int i=1;i<=n;i++) 46 f[i]=i; 47 48 int tot=0; 49 50 int k=0; 51 52 for(int i=1;i<=m;i++) 53 { 54 55 if(Find(edge[i].x) != Find(edge[i].y)) 56 { 57 join(edge[i].x,edge[i].y); 58 tot+=edge[i].z; 59 k++; 60 } 61 if(k==n-1) break; 62 63 } 64 65 printf("%d",tot); 66 }
克鲁斯卡尔算法使用的了
并查集思想
快速排序
优先连权值更大的边
复杂度为 O(mlogm)
而普利姆算法则采用dp思想,复杂度为 O(N^2),优化后达到 O(mlogn)
综合比对来看
1°Prim在稠密图中比Kruskal优,稀疏图劣
2°Prim在任何情况下均有令人满意的时间复杂度,代价就是空间复杂度极高,并且代码极其恶心
但是时间复杂度不能说明一个算法的优劣性质
所以考试,你想用啥就用啥。