1,运营商的挑战:

1,在下图标出的城市间架设一条通信线路;

2,要求:

1,任意两个城市间都能够通信;

2,将架设成本降至最低;

2,问题抽象:

1,如何在图中选择 n - 1 条边使得 n 个顶点间两两可达,并且这 n - 1 条边的权值之和最小?

3,最小(大)生成树:

1,仅使用图中的 n - 1 条边连接图中的 n 个顶点;

2,不能使用产生回路的边;

3,各边上的权值总和达到最小(大);

4,寻找最小生成树:

5,使用 prim 方法手工寻找最小生成树:

6,最小生成树算法步骤(prim):

1,选择某一顶点 v0 作为起始顶点,使得 T = {v0},F = {v1, v2, ..., vn},E = {};

2,每次选择一条边,这条边是所有(u, v)中权值最小的边,且 u 属于 T,v 属于 F;

3,修改 T,F,E:

T = T + {v}, F = F - {v}, E = E + {(u, v)}

4,当 F != NULL 时,且(u, v)存在,转 2;否则,结束;

7,最小生成树算法的原材料:

1,如果 T 集合到 F 集合中同一个顶点的连接有多条,那么选取权值最小的连接;

8,最小生成树算法流程图:

9,注意事项:

1,最小生成树仅针对无向图有意义;

2,必须判断图对象是否能够看做无向图;

1,如果可以才能够用 prim 算法;

10,有向图可看做无向图的充分条件:

1,有向的任意两顶点之间若存在连接,则两顶点相互可达、权值相等;

11,图类型(Graph)中的新增成员函数:

1,virtual bool isAdjacent(int i, int j) = 0;

1,判断在当前图中顶点 i 到顶点 j 是否邻接;

2,bool asUndirected();

1,判断当前的有向图是否能够看做无向图;

12,最小生成树 prim 算法实现:

  1.  1,判断邻接的实现:
      1,邻接矩阵的实现:
  1. /* 判断 i 到 j 顶点边是否连接,值不为空就连接 */
  2. bool isAdjacent(int i, int j)
  3. {
  4. return ( <= i) && (i < vCount()) && ( <= j) && (j < vCount()) && (m_edges[i][j] != NULL);
  5. }
  1.   2,邻接链表的实现:
  1. bool isAdjacent(int i, int j)
  2. {
  3. return ( <= i) && (i < vCount()) && ( <= j) && (j < vCount()) && (m_list.get(i)->edge.find(Edge<E>(i, j)) >= );
  4. }

   2,判断是否为无向图的实现:

  1.    /* 最小生成树只能用于无向图,而我们针对的是有向图,所以要判断有向图什么时候能够被当做无向图 */
  2. bool asUndirected()
  3. {
  4. bool ret = true;
  5.  
  6. for(int i=; i<vCount(); i++)
  7. {
  8. for(int j=; j<vCount(); j++)
  9. {
  10. /* i 到 j 是连接的,并且 j 到 i 也是连接的,然后权值也要相等 */
  11. if( isAdjacent(i, j) )
  12. {
  13. ret = ret && isAdjacent(j, i) && (getEdge(i, j) == getEdge(j, i));
  14. }
  15. }
  16. }
  17.  
  18. return ret;
  19. }

  3,Prim算法实现:

  1.    /* Prim 法实现最小、大生成树;返回值是数组,因为最小、大生成树的结果就是一系列的边,所以返回边的数组;参数表示理论上的最大权值; */
  2. SharedPointer< Array< Edge<E> > > prim(const E& LIMIT, const bool MINIMUM = true) // 返回一个指向存储边的数组指针
  3. {
  4. LinkQueue< Edge<E> > ret; // 返回边队列,本质是 E 集合
  5.  
  6. /* 执行 prim 算法 */
  7. if( asUndirected() ) // 无向图
  8. {
  9. DynamicArray<int> adjVex(vCount());//保存最小权值的边的 F集合中顶点
  10. DynamicArray<bool> mark(vCount()); // 保存 T 集合或者 F 集合的标记
  11. DynamicArray<E> cost(vCount()); // 保存最小权值的顶点中 E 集合中顶点,寻找最小值要配合 mark 使用
  12. SharedPointer< Array<int> > aj = NULL; // 保存某个顶点邻接数组
  13. bool end = false; // 用于标记判断 prim 是否要中断执行
  14. int v = ; // 代表习惯性的从 0 顶点生成最小生成树
  15.  
  16. /* 执行初始化 */
  17. for(int i=; i<vCount(); i++)
  18. {
  19. adjVex[i] = -; // 没有边被访问
  20. mark[i] = false; // 顶点都没有被访问
  21. cost[i] = LIMIT; // 参数传递理论上的最大权值
  22. }
  23.  
  24. mark[v] = true; // 初始顶点做标记
  25.  
  26. aj = getAdgacent(v); // 获取初始顶点的邻接顶点
  27.  
  28. /* 设置初始顶点对应的位置 */
  29. for(int j=; j<aj->length(); j++)
  30. {
  31. cost[(*aj)[j]] = getEdge(v, (*aj)[j]); // 保存/到对应顶点的响应权值
  32. adjVex[(*aj)[j]] = v; // 记录权值所对应的顶点,即能够得到边
  33. }
  34.  
  35. /* 真正循环找边 */
  36. for(int i=; (i<vCount()) && !end; i++) // 最多循环顶点次;也可能条件不满足,提前结束,所以有 !end
  37. {
  38. E m = LIMIT;
  39. int k = -; // 记录最小值的顶点
  40.  
  41. /* 通过 cost 数组找最小值 */
  42. for(int j=; j<vCount(); j++)
  43. {
  44. if( !mark[j] && (MINIMUM ? (cost[j] < m) : (cost[j] > m)) ) // !makr[j] 条件是因为选取最小权值时本质上是选取连接的最小边,对应的顶点是在 F集合,此时 mark 中对应的值为假当中的,则在 mark 数组中对应的就为假,所以要这个条件,这里有最小值最大值的设置
  45. {
  46. m = cost[j];
  47. k = j; // 得到记录的最小值的顶点号
  48. }
  49. }
  50. end = (k == -); // 是否找到合法最小权值,因为有可能在上面 if 条件中没有找到合法的最小权值
  51. if( !end )
  52. {
  53. ret.add(Edge<E>(adjVex[k], k, getEdge(adjVex[k], k))); // 在 adjVex 中找到这条边
  54.  
  55. mark[k] = true; // 标记顶点进入了 T 集合
  56.  
  57. aj = getAdgacent(k); // 找新的集合连接
  58.  
  59. /* 找到之后更新 cost 数组和 adgVex 数组 */
  60. for(int j=; j<aj->length(); j++)
  61. {
  62. if( !mark[(*aj)[j]] && (MINIMUM ? (getEdge(k, (*aj)[j]) < cost[(*aj)[j]]) : (getEdge(k, (*aj)[j]) > cost[(*aj)[j]])) ); //只对 F 集合操作
  63. {
  64. cost[(*aj)[j]] = getEdge(k ,(*aj)[j]); //如果 T 到 F 集合新连接权值较小,则记录到 cost 数组中,新加入的点 k 和之前 T 集合里的点到 F 集合里的点的权值要比较呢;如果在 k 到 F 集合中找不到合适的点,则用T中的点代替
  65. adjVex[(*aj)[j]] = k; // 将最小权值的起始点设入到邻接边中
  66. }
  67. }
  68. }
  69. }
  70. }
  71. else
  72. {
  73. THROW_EXCEPTION(InvalidOperationException, "Prim operation is for undirected graph only ...");
  74. }
  75.  
  76. /* 判断边的数目是否够,即 n-1 条边 */
  77. if( ret.length() != (vCount() - ) )
  78. {
  79. THROW_EXCEPTION(InvalidOperationException, "No enough edge for prim operation ...");
  80. }
  81. return toArray(ret); // 返回值是边的数组
  82. }

14,prim 算法测试代码:

  1. #include <iostream>
  2. #include "MatrixGraph.h"
  3. #include "ListGraph.h"
  4.  
  5. using namespace std;
  6. using namespace DTLib;
  7.  
  8. template< typename V, typename E >
  9. Graph<V, E>& GraphEasy()
  10. {
  11.    static MatrixGraph<, V, E> g;
  12.  
  13. g.setEdge(, , );
  14. g.setEdge(, , );
  15. g.setEdge(, , );
  16. g.setEdge(, , );
  17. g.setEdge(, , );
  18. g.setEdge(, , );
  19. g.setEdge(, , );
  20. g.setEdge(, , );
  21. g.setEdge(, , );
  22.    g.setEdge(, , );
  23.  
  24. return g;
  25. }
  26.  
  27. template< typename V, typename E >
  28. Graph<V, E>& GraphComplex()
  29. {
  30.    static ListGraph<V, E> g();
  31.  
  32. g.setEdge(, , );
  33. g.setEdge(, , );
  34. g.setEdge(, , );
  35. g.setEdge(, , );
  36. g.setEdge(, , );
  37. g.setEdge(, , );
  38. g.setEdge(, , );
  39. g.setEdge(, , );
  40. g.setEdge(, , );
  41. g.setEdge(, , );
  42. g.setEdge(, , );
  43. g.setEdge(, , );
  44. g.setEdge(, , );
  45. g.setEdge(, , );
  46. g.setEdge(, , );
  47. g.setEdge(, , );
  48. g.setEdge(, , );
  49. g.setEdge(, , );
  50. g.setEdge(, , );
  51. g.setEdge(, , );
  52. g.setEdge(, , );
  53. g.setEdge(, , );
  54. g.setEdge(, , );
  55. g.setEdge(, , );
  56. g.setEdge(, , );
  57. g.setEdge(, , );
  58. g.setEdge(, , );
  59. g.setEdge(, , );
  60. g.setEdge(, , );
  61.    g.setEdge(, , );
  62.  
  63. return g;
  64. }
  65.  
  66. int main()
  67. {
  68. Graph<int, int>& g = GraphEasy<int, int>();
  69.    SharedPointer< Array< Edge<int> > > sa = g.prim();
  70.  
  71.    int w = ;
  72.  
  73. for(int i=; i<sa->length(); i++)
  74. {
  75. w += (*sa)[i].data;
  76.  
  77. cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl;
  78.    }
  79.  
  80.    cout << "Weight: " << w << endl;
  81.  
  82. return ;
  83. }

15,小结:

1,最小生成树使得顶点间的连通代价最小;

2,Prim 算法通过顶点的动态标记寻找最小生成树;

3,Prim 算法的关键是集合概念的运用(T 集合,F 集合);

4,利用 Prim 算法的思想也能寻找图的“最大生成树”;

图——图的Prim法最小生成树实现的更多相关文章

  1. c/c++ 图的创建及图的相关函数(链表法)

    c/c++ 图的创建及图的相关函数(链表法) 图的概念 图由点和线组成 知道了图中有多少个点,和哪些点之间有线,就可以把一张图描绘出来 点之间的线,分有方向和无方向 创建图 创建图,实际就是创建出节点 ...

  2. 随机Prim法创建随机迷宫(C#实现)

    因为这两天想参加一个比赛,所以就在上网找素材,刚好看到了迷宫生成,就决定拿这个开刀了. 参考的原文地址为(来源页面) 源地址中是使用AS实现的,没学过AS,所以直接不会运行,于是就自己根据原文的概念进 ...

  3. 图——图的Kruskal法最小生成树实现

    1,最小生成树的特征: 1,选取的边是图中权值较小的边: 2,所有边连接后不构成回路: 2,prim 算法是以顶点为核心的,最下生成树最大的特征是边,但 prim 算法非要以顶点为核心来进行,有些复杂 ...

  4. 6)图[2]Prim算法[最小生成树]

    Prim 算法 求解方法: 首先将所指定的起点作为已选顶点,然后反复在满足如下条件下的边中选择一条最小边,直到 所有顶点已成为已选顶点为止(选择n-1条边). #include "iostr ...

  5. matlab学习——04图与网络(最短路,最小生成树,最大流)

    04图与网络 1.最短路 (1) 自己写的dijstra算法 format compact; clc,clear all a=zeros(6); a(1,2)=50;a(1,4)=40;a(1,5)= ...

  6. 图——图的Floyd法最短路径实现

    1,Dijkstra 算法一次性求得起始顶点到所有其它顶点的最短路径,如果想要求解任意两个顶点之间的最短路径,可将图中顶点作为起始顶点执行 n 次 Dijkstra 算法就可以了: 2,可能解决方案: ...

  7. 图——图的Dijkstra法最短路径实现

    1,最短路径的概念: 1,从有向图中某一顶点(起始顶点)到达另一顶点(终止顶点)的路径中,其权值之和最小的路径: 2,问题的提法: 1,给定一个带权有向图 G 与起始顶点 v,求从 v 到 G 中其它 ...

  8. Prim求解最小生成树

    #include "ljjz.h" typedef struct edgedata /*用于保存最小生成树的边类型定义*/ { int beg,en; /*beg,en是边顶点序号 ...

  9. Prim Algoritm(最小生成树)

    Prim Algorithm.这个算法可以分为下面几个步骤: 将顶点集V分成两个集合A和B,其中集合A表示目前已经在MST中的顶点,而集合B则表示目前不在MST中的顶点. 在B寻找与集合A连通的最短的 ...

随机推荐

  1. [51nod 1830] 路径交

    问题描述 给定一棵n个点的树,以及m条路径,每次询问第L条到第R条路径的交集部分的长度(如果一条边同时出现在2条路径上,那么它属于路径的交集). 输入格式 第一行一个数n(n<=500,000) ...

  2. CF1260F

    题目大意 一棵树,每个节点的权为L[i]~R[i],一棵树的贡献为\(\sum\limits_{h_{i} = h_{j}, 1 \le i < j \le n}{dis(i,j)}\),其中\ ...

  3. 阿里云移动研发平台 EMAS 助力银行业打造测试中台,提升发版效能

    随着移动互联网的发展,手机银行凭借低成本.操作简单.不受时间空间约束等优势,正逐步替代传统的网银交易方式.越来越多的银行开始了“业务移动化”转型之路,“手机APP”已经成为企业价值传递和关系维护的关键 ...

  4. php将base64字符串转换为图片

    昨天用一个js插件 [链接]: http://www.erdangjiade.com/js/910.html 实行了图片裁剪并预览,不过它生产的图片资源是一个base64字符串,不好保存后来在网上找到 ...

  5. Linux基础教程 linux系统中的批量删除文件与空文件删除的命令介绍

    linux下面删除文件或者目录命令rm(remove): 兄弟连Linux培训 功能说明:删除文件或目录. 语 法:rm[-dfirv][--help][--version][文件或目录...] 补充 ...

  6. k8s的node节点,执行kubectl get XXX报错

    报错现象: [root@localhost ~]# kubectl get nodes The connection to the server localhost:8080 was refused ...

  7. 【BZOJ2022】Pku1837 Balance

    Description Gigel has a strange "balance" and he wants to poise it. Actually, the device i ...

  8. 如何将python源文件打包成exe文件

    PyInstaller是一个十分有用的第三方库,它能够在Windows.Linux.Mac OS X 等操作系统下将 Python 源文件打包,通过对源文件打包,Python 程序可以在没有安装 Py ...

  9. 【CF1252L】Road Construction(基环树,最大流)

    题意:给定一张n点n边无重边自环的无向图,刚开始每条边都没有被选择,每条边上有一个颜色集合,必须从中选择一种 有K个工人,每个工人有颜色a[i],需要把工人分配到与其颜色相同的边上 问是否能有一种使得 ...

  10. HDU 1711 Number Sequence(KMP)附带KMP的详解

    题目代号:HDU 1711 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 Number Sequence Time Limit: 10000/ ...