要了解最小生成树的概念,我们首先要知道生成树是什么
生成树的定义
一个有 n 个结点的联通图的生成树是原图的极小连通子图,生成树包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
最小生成树的性质
1.容易想象,要保证n个节点联通,至少要有n-1条边,所以一个有n个节点的生成树必有n-1条边。
2. 在所有生成树中,最小生成树的权值之和是最小的。
3. 再添加任意一条边,都将造成回路。
MST性质
描述:假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
MST算法告诉我们,可以通过不断确定最小权值的边来获取最小生成树。下面介绍两种根据MST性质,构造最小生成树的算法。
Prim算法
Prim算法的指导思想是通过不断选择点来构建生成树。设图N = {V,{E}},用集合U来表示生成树的顶点的集合,一开始,U为空集。将V任意一个点添加到U中,然后重复以下操作
1. 从未加入U的顶点,即V-U中,选择出距离U最近的顶点。这里的距离指的是V-U中一个顶点和U中所有顶点距离的最小值。
2. 将该顶点加入到U中,同时更新其他未加入U的顶点到U的最短距离。
3. 如果V中所有顶点都加入U则停止,否则返回步骤1。
Prim算法是从一个顶点集合不断扩展,按照边的权重大小选择加入这个集合的点。
代码实现
#include<iostream> #include<vector> using namespace std; const int inf = 1e9+5; int Edge[1005][1005]; // 用邻接矩阵来表示图 int n,m; int dist[1005]; //用来记录到顶点集合U的距离 vector<bool>vis(1005,false); //Prim算法 int Prim() { int sum = 0; fill(dist,dist+1005,inf); dist[1] = 0; // 将0加入到U中 for(int i=1;i<=n;i++) { int mindist = inf,k; for(int j=1;j<=n;j++) { if(!vis[j]&&mindist>dist[j]) { k = j; mindist = dist[j]; } } vis[k] = 1; sum +=dist[k]; printf("Choose the vertex:%d\n",k); for(int j=1;j<=n;j++) { if(!vis[j]&&Edge[k][j]<dist[j]) { dist[j] = Edge[k][j]; } } } return sum; } int main() { scanf("%d %d",&n,&m); int s,e,v;// 构件图 fill(Edge[0],Edge[0]+1005*105,inf); for(int i=0;i<m;i++) { scanf("%d %d %d",&s,&e,&v); Edge[s][e] = Edge[e][s] = v; } int sum = Prim(); printf("The sum of MST:%d\n",sum); return 0; }
Kruskal算法
Kruskal的指导思想是将,通过不断选择边来构建生成树。设图N={V,{E}},V中有顶点n个,则初始化n个集合,U1={V1},U2={V2},...,Un={Vn},然后进行如下操作:
1. 从{E}中选择端点在不同集合中权值最小的边。
2. 合并这两个边所在的集合。
3. 如果所有集合都合并为一个集合则停止,否则返回步骤1。
Kruskal算法是将原有的顶点集合按照边的权重大小不断合并,最终形成现有的集合。
代码实现:
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 int n,m; 5 struct Edge{ 6 int s,t,v; 7 Edge(int s,int t,int v):s(s),t(t),v(v) 8 { 9 } 10 Edge(){} 11 bool operator<(const Edge&a) 12 { 13 return v<a.v; 14 } 15 }E[1005*1005]; 16 int pre[1005]; 17 void init() 18 { 19 for(int i=1;i<=n;i++) 20 pre[i] = i; 21 } 22 int find_pre(int x) 23 { 24 if(x==pre[x]) 25 return x; 26 else 27 return pre[x] = find_pre(pre[x]); 28 } 29 void Union(int x,int y) 30 { 31 int rootx = find_pre(x),rooty = find_pre(y); 32 if(rootx!=rooty) 33 pre[rootx] = rooty; 34 } 35 int main() 36 { 37 scanf("%d %d",&n,&m); 38 init(); 39 int s,t,v; 40 for(int i=0;i<m;i++) 41 { 42 scanf("%d %d %d",&s,&t,&v); 43 E[i] = Edge(s,t,v); 44 } 45 sort(E,E+m); 46 int cnt = 0,sum = 0; 47 for(int i=0;i<m;i++) 48 { 49 if(find_pre(E[i].s)!=find_pre(E[i].t)) 50 { 51 printf("Choose the edge: %d %d\n",E[i].s,E[i].t); 52 Union(E[i].s,E[i].t); 53 cnt++; 54 sum+=E[i].v; 55 } 56 if(cnt==n-1) 57 break; 58 } 59 printf("The sum of MST:%d",sum); 60 return 0; 61 }