基本概念:
树:如果一个无向连通图G中不存在回路,则称图G是一颗树。
生成树:无向连通图G的一个子图如果是一颗包含了G中所有顶点的树,则称它为图G的生成树。
注意:生成树是图G的极小连通子图,表示在若在图中任添加一条边都将形成一个回路,同样的,若任意去掉一条边都将使图不在连通。
如果在边中加上权值,那么权值最小的生成树即为最小生成树,权值最大的生成树为最大生成树。
最小生成树算法:
由生成树的定义不难知道,BFS或DFS不重复的遍历结果就是一个生成树。通常情况下,我们都是要求最小生成树的。求最小生成树的算法有三种:Kruskal算法,Boruvka算法,Prime算法。常用的是Kruskal算法和Prime算法。
Kruskal算法核心思想:作用与边,每次选取当前可用的最小权值的边。
Kruskal算法有两个注意的地方:① 当前可用,即若某边的权值最小,但它关联的两个顶点本省已经在同一个集合里了也是不可选的。
② 最小权值,这个是必须的,不多说。
Prime算法核心思想:作用与顶点,通过选择当前可用的最小权值的边把其他顶点加入到生成树当中。
算法过程:
1.将一个图的顶点分为两部分,一部分是最小生成树中的结点(A集合),另一部分是未处理的结点(B集合)。
2.首先选择一个结点,将这个结点加入A中,然后,对集合A中的顶点遍历,找出A中顶点关联的边权值最小的那个B中的结点(设为v),将此顶点从B中删除,加入集合A中。
3.重复步骤2,直到B集合中的结点为空,结束此过程。
4.A集合中的结点就是由Prime算法得到的最小生成树的结点,依照步骤2的结点连接这些顶点,得到的就是这个图的最小生成树。
代码:
1 int mat[N][N]; 2 int lowcost[N]; 3 int pre[N]; 4 int n, m; 5 bool used[N]; 6 7 int Prime(int s) 8 { 9 for(int i=1; i<=n; i++) 10 lowcost[i] = mat[s][i], pre[i]=s; 11 used[s] = true; 12 lowcost[s] = 0; 13 int mst=0, cnt=0; 14 for(int i=1; i<n; i++) 15 { 16 int tmp=INF, k; 17 for(int j=1; j<=n; j++) 18 if(!used[j] && lowcost[j]<tmp) 19 tmp = lowcost[k=j]; 20 if(tmp==INF) break; 21 used[k] = true; 22 // 输出最小生成树中边 23 printf("%d %d %d\n", k, pre[k], lowcost[k]); 24 mst += tmp; 25 if(++cnt ==n-1) break; 26 for(int j=1; j<=n; j++) 27 if(!used[j] && mat[k][j] < lowcost[j]) 28 lowcost[j] = mat[k][j], pre[j]=k; 29 } 30 return mst; 31 }