最小生成树

末蓝、 2022-09-20 15:18 310阅读 0赞

最小生成树定义:

在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即(u, v)\\in E),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即T\\subseteq E )且为无循环图,使得w(T) = \\sum\_\{(u,v)\\in T\} w(u,v)的 w(T) 最小,则此 T 为 G 的最小生成树。

最小生成树其实是最小权重生成树的简称。

性质:

  • 最小生成树的边数必然是顶点数减一,|E| = |V| - 1。
  • 最小生成树不可以有循环。
  • 最小生成树不必是唯一的。

算法;

Prim算法与Kruskal算法是寻找最小生成树的经典方法,两者皆为贪心法,通常使用二元堆积,时间复杂度为 O(E\\lg V)。若使用斐波那契堆,Prim算法可加速至 O(E + V\\lg V)
注:以上内容来自维基百科(为2014年6月19日 (星期四) 12:01修订后结果)

接下来,博主采用的是prim算法,用的是邻接矩阵,算法复杂度为o(V^2)(V为点组成的集合),具体的prim算法请点“Prim算法”

后面博主又补充了Kruskal算法,其实还有Borůvka’s algorithm,后续补充吧…..

源代码如下:

以下代码图中的点不得超过104个,单边权值不得超过1000000

  1. #include<stdio.h>
  2. #define N 105
  3. #define INF 1000000
  4. int map[N][N],visit[N],dis[N];
  5. int main()
  6. {
  7. int n,x,y,value,i,j;
  8. scanf("%d",&n);//输入的图的点不超过104个
  9. for(i=1;i<=n;i++)
  10. {
  11. for(j=1;j<=n;j++)
  12. map[i][j]=INF;
  13. visit[i]=0;
  14. }
  15. while(scanf("%d%d%d",&x,&y,&value)==3&&x&&y&&value//输入图中点的关系
  16. {
  17. map[x][y]=value;
  18. map[y][x]=value;//输入的图中的点的关系默认为双向的
  19. }
  20. visit[1]=1;//默认从点1开始,把点1移向集合V
  21. int m_coor=0,min=INF;
  22. for(i=2;i<=n;i++)
  23. {
  24. dis[i]=map[1][i];
  25. if(min>dis[i])
  26. {
  27. min=dis[i];
  28. m_coor=i;//找出下一个移向集合V的点的坐标
  29. }
  30. }
  31. visit[m_coor]=1;//标记该点移到了集合V
  32. if(min==INF)
  33. {
  34. printf("No minimum spanning tree!\n");
  35. return 0;
  36. }//集合V中的点与集合E中的点不连接,图不是连通的,因此不存在最小生成树
  37. int count=n-1;
  38. while(count-->0)//将右侧集合中的点依次移到左侧集合
  39. {
  40. for(i=2;i<=n;i++)
  41. if(visit[i]==0)
  42. {
  43. if(dis[i]>map[m_coor][i])
  44. dis[i]=map[m_coor][i];
  45. }
  46. min=INF;
  47. for(i=2;i<=n;i++)
  48. if(visit[i]==0&&min>dis[i])
  49. {
  50. min=dis[i];
  51. m_coor=i;
  52. }
  53. if(min==INF)
  54. {
  55. printf("No minimum spanning tree!\n");
  56. return 0;
  57. }
  58. visit[m_coor]=1;
  59. }
  60. int sum=0;
  61. for(i=2;i<=n;i++)
  62. sum+=dis[i];
  63. printf("%d\n",sum);//输出最小生成树
  64. return 0;
  65. }

改进后的prim,更整齐

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #define mini 1000005
  5. #define N 105
  6. using namespace std;
  7. int dis[N][N],dist[N],sign[N];
  8. void intial()
  9. {
  10. for(int i=0;i<N;i++)
  11. {
  12. dist[i]=mini;
  13. sign[i]=0;
  14. }
  15. }
  16. int main()
  17. {
  18. int n;
  19. // freopen("in.txt","r",stdin);
  20. // freopen("out1.txt","w",stdout);
  21. while(scanf("%d",&n)!=EOF)
  22. {
  23. int i,j;
  24. for(i=0;i<n;i++)
  25. for(j=0;j<n;j++)
  26. scanf("%d",&dis[i][j]);
  27. intial();
  28. int min,m_coor=0,coor=0,time=n-1,sum=0;sign[0]=1;
  29. while(time--) //一定是n-1次循环,不然就会出错
  30. {
  31. min=mini;
  32. for(i=1;i<n;i++)
  33. if(!sign[i])
  34. {
  35. if(dist[i]>dis[m_coor][i])dist[i]=dis[m_coor][i];
  36. if(min>dist[i])
  37. {
  38. min=dist[i];coor=i;
  39. }
  40. }
  41. sign[coor]=1;
  42. m_coor=coor;
  43. sum+=min;
  44. }
  45. printf("%d\n",sum);
  46. }
  47. return 0;
  48. }

kruskal算法求最小生成树

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cstdlib>
  4. #include <algorithm>
  5. using namespace std;
  6. const int maxn=1000000;
  7. int pa[maxn];
  8. struct Eage
  9. {
  10. int x,y,longth;
  11. void read()
  12. {
  13. scanf("%d%d%d",&x,&y,&longth);
  14. }
  15. bool operator < (const Eage &t)const
  16. {
  17. return longth<t.longth;
  18. }
  19. };
  20. Eage eage[maxn];
  21. void initial(int n)
  22. {
  23. for(int i=0;i<=n;i++)
  24. pa[i]=i;
  25. }
  26. int find(int x)
  27. {
  28. return x==pa[x]? x:(pa[x]=find(pa[x]));
  29. }
  30. bool Merge(int x,int y)
  31. {
  32. int px=find(x),py=find(y);
  33. if(px!=py)
  34. {
  35. pa[px]=py;
  36. return true;
  37. }
  38. return false;
  39. }
  40. /*bool cmp(Eage a,Eage b)
  41. {
  42. return a.longth<b.longth;
  43. }*/
  44. int main()
  45. {
  46. int n,m;
  47. while(scanf("%d%d",&n)==2&&n)
  48. {
  49. initial(n);
  50. for(int i=0;i<m;i++)
  51. eage[i].read();
  52. //scanf("%d%d%d",&eage[i].x,&eage[i].y,&eage[i].longth);
  53. int sum=0,count=0;
  54. sort(eage,eage+m);
  55. for(int i=0;i<m;i++)
  56. {
  57. if(Merge(eage[i].x,eage[i].y))
  58. {
  59. sum+=eage[i].longth;
  60. count++;
  61. }
  62. }
  63. if(count!=n-1)printf("No minimum spaning tree!\n");
  64. else printf("%d\n",sum);
  65. }
  66. return 0;
  67. }

发表评论

表情:
评论列表 (有 0 条评论,310人围观)

还没有评论,来说两句吧...

相关阅读

    相关 生成

    最小生成树定义: 在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即![(u, v)\\in E][u_ v_in E]),而

    相关 生成

    一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 最小生成树在n个顶点的情形下,有n-1条边。生成树是对连

    相关 生成

    有n个城市,m条道路,现在输入n到m的道路的长度,用最少的边将图连接,输出让图连接的最小值 这道题我研究了好长时间才把答案看明白,现在给大家分享一下 具体代码如下

    相关 生成

    问题提出: 要在n个城市间建立通信联络网。顶点:表示城市,权:城市间通信线路的花费代价。希望此通信网花费代价最小。 问题分析: 答案只能从生成树中找,因为要做到任何

    相关 生成

    kruskal算法基本思路:先对边按权重从小到大排序,先选取权重最小的一条边,如果该边的两个节点均为不同的分量,则加入到最小生成树,否则计算下一条边,直到遍历完所有的边。 p

    相关 生成

    最小生成树 什么是最小生成树 给定一张无向带权图G=(V,E,W),从中挑选出|V|-1条边,使得V中任意两点都有直接或者间接的路径 显然,最后得到的子