• 图的应用4.0-----关键路径(AOE网)


    目录

    前言

    AOE网

    1.基本概念

    2.应用

    关键路径

    1.算法理论

    2.代码实现(C/C++)


    前言

            前面学习了图AOV网的应用,即拓扑排序,那这一期我们学习AOE网的应用,这是一个图的一个很广泛问题,也就是关键路径。那什么是关键路径呢?下面就一起来看看。

     数据结构----图的应用四部曲
    最小生成树问题:图的应用1.0-----最小生成树问题-CSDN博客
    最短通路问题(Djkstra算法):图的应用2.0-----最短通路问题(Dijkstra和Floyd算法)-CSDN博客

    拓扑排序:图的应用3.0-----拓扑排序-CSDN博客
    关键路径算法图的应用4.0-----关键路径(AOE网)-CSDN博客

    AOE网

    1.基本概念

            把工程计划表示为边表示活动的网络,即AOE网用顶点表示事件,弧表示活动,弧的权表示活动持续时间。

            事件表示在它之前的活动已经完成,在它之后的活动可以开始

     如图所示,这就是一个AOE网的问题

    任意应该AOE网都有一个起点和终点,那这里就称作为源点和汇点。 

    源点与汇点

    源点:入度为0的点,表示一个工程的开始。

    汇点:出度为0的点,表示一个工程的结束。

    2.应用

    对于AOE网,我们关心两个问题

    1. 完成整项工程至少需要多少时间?
    2. 哪些活动是影响工程进度的关键?

    AOE网可以很好的表示一项工程或者项目经历的流程和时间,那现在提出一个问题:如何从起点到终点经历过的总时间最长呢?(完成这个工程需要的最长时间)

    这里就要用到关键路径的算法了,下面我们就来看看关键路径是怎么q求的。

    关键路径

    关键路路径-----路径长度最长的路径
    路径长度------路径上各活动持续时间之和。

    1.算法理论

     以这个图为示例,下面要弄懂4个数组数据的值

    1.  ve(vj)------表示事件 vj的最早发生时间

             例: ve(v1) = 0 ve(v2) = 30

    2.vl(vj)-----表示事件 vj 的最迟发生时间

             例: vl(v4) = 165

    3.e(i)-----表示活动ai的最早开始时间

            例:e(a3) = 30

    4.l(i)-----表示活动ai 的最迟开始时间

             例: l(a3) = 120

    l(i) - e(i)-表示完成活动 ai 的时间余量。 例: (3) - e(3) = 90

    关键活动

    关键活动关键路径上的活动,即 l(i) == e(i) (即 l(i) - e(i) ==0 ) 的活动

    以上4个量的关系: 

    1.如何找l)== e(i)的关键活动?

    设活动 ai 用弧 表示,其持续时间记为: Wj,k

    则有: (1) e(i) = ve(j)

             (2) l(i) = vl(k) - Wj, k

    2.如何求 ve(j)和 vl(j) ?

    (1) 从 ve(1)=0开始向前递推

            ve(j) = Max{ve(i) + Wij}, < i,j >属于T, 2 <=j <=n

            其中T是所有以i为头的弧的集合。

    (2)从 vl(n)= ve(n)开始向后递推

            vl(i) = Min{vl(j) - Wij}, < i, j >属于S, 1<=i<=n -1

            其中 S 是所有以i为尾的弧的集合。

    注意:最短路径与最小生成树不同,路径上不一定包含 n个顶点,也不定包含 n-1条边 

    求解过程,以下图为案例求解ve、vl、e、和l数组。

    求关键路径步骤:

    1. 求 ve(i)、vl(j)
    2. 求e(i)、l(i)
    3. 计算 l(i) - e(i),如果值为0那就是关键路径中的一条路径
    4. 套用以下公式即可

    求得的ve和vl如图所示:

    求得的e和l,如图所示:

    2.代码实现(C/C++)

    邻接矩阵图的结构体:

    1. #define Maxint 32767
    2. #define Maxnum 100//最大顶点数
    3. //数据类型
    4. typedef struct datatype {
    5. char id[10];
    6. //……
    7. }
    8. ElemType;
    9. //图的邻接数组
    10. typedef struct graph {
    11. ElemType vexs[Maxnum];//图数据
    12. int matrix[Maxnum][Maxnum];//二维数组矩阵
    13. int vexnum;//点数
    14. int arcnum;//边数
    15. }Graph;

    关键路径算法代码: 

    1. //获取入度为0的起始点下标
    2. int in_degree0(Graph G) {
    3. int* sum_degree = (int*)malloc(sizeof(int) * G.vexnum);
    4. memset(sum_degree, 0, sizeof(int) * G.vexnum);
    5. for (int x = 0; x < G.vexnum; x++) {
    6. for (int y = 0; y < G.vexnum; y++) {
    7. if (G.matrix[y][x] != 0 && G.matrix[y][x] != Maxint) {
    8. sum_degree[x]++;
    9. }
    10. }
    11. }
    12. for (int i = 0; i < G.vexnum; i++) {
    13. if (sum_degree[i] == 0) {
    14. free(sum_degree);
    15. sum_degree = NULL;
    16. return i;
    17. }
    18. }
    19. }
    20. //获取出度为0的交汇点下标
    21. int out_degree0(Graph G) {
    22. int* sum_degree = (int*)malloc(sizeof(int) * G.vexnum);
    23. memset(sum_degree, 0, sizeof(int) * G.vexnum);
    24. for (int x = 0; x < G.vexnum; x++) {
    25. for (int y = 0; y < G.vexnum; y++) {
    26. if (G.matrix[x][y] != 0 && G.matrix[x][y] != Maxint) {
    27. sum_degree[x]++;
    28. }
    29. }
    30. }
    31. for (int i = 0; i < G.vexnum; i++) {
    32. if (sum_degree[i] == 0) {
    33. free(sum_degree);
    34. sum_degree = NULL;
    35. return i;
    36. }
    37. }
    38. }
    39. //队列的结构体
    40. typedef struct q {
    41. int prev;//上一个顶点下标
    42. int next;//下一个顶点下标
    43. }Queue;
    44. //边的结构体 起点 长度 终点
    45. typedef struct e {
    46. int begin_index;//这个边的起始点
    47. int e;//边长度
    48. int end_index;//这个边的终点
    49. }E;
    50. //关键路径算法AOE
    51. void Critical_path(Graph G) {
    52. //申请ve,vl,e,l的空间
    53. int* ve = (int*)malloc(sizeof(int) * G.vexnum);
    54. int* vl = (int*)malloc(sizeof(int) * G.vexnum);
    55. E* e = (E*)malloc(sizeof(E) * G.arcnum);
    56. int* l = (int*)malloc(sizeof(int) * G.arcnum);
    57. Queue* queue = (Queue*)malloc(sizeof(Queue) * G.vexnum);
    58. //01--处理ve
    59. for (int k = 0; k < G.vexnum; k++) {
    60. queue[k].prev = queue[k].next = -1;
    61. ve[k] = 0;
    62. }
    63. //对第一个数据处理
    64. int begin = in_degree0(G);
    65. //初始化
    66. int qu_count = 0;
    67. queue[qu_count].prev = queue[qu_count].next = begin;
    68. qu_count++;
    69. //后继处理
    70. while (qu_count) {
    71. //出队操作
    72. Queue pop = queue[0];
    73. //后继数据前移
    74. for (int d = 0; d < qu_count - 1; d++) {
    75. queue[d] = queue[d + 1];
    76. }
    77. qu_count--;
    78. //获取到出队顶点最早开始时间ve
    79. int max = ve[pop.prev] + G.matrix[pop.prev][pop.next];
    80. if (ve[pop.next] <= max)
    81. ve[pop.next] = max;
    82. //把当前出队的顶点后继连接的顶点入队
    83. for (int j = 0; j < G.vexnum; j++) {
    84. if (G.matrix[pop.next][j] != 0 && G.matrix[pop.next][j] != Maxint) {
    85. queue[qu_count].next = j;
    86. queue[qu_count].prev = pop.next;
    87. qu_count++;
    88. }
    89. }
    90. }
    91. //02--处理vl
    92. int end = out_degree0(G);
    93. for (int k = 0; k < G.vexnum; k++) {
    94. queue[k].prev = queue[k].next = -1;
    95. vl[k] = ve[end];
    96. }
    97. //对第一个数据处理
    98. queue[qu_count].next = queue[qu_count].prev = end;
    99. qu_count++;
    100. while (qu_count) {
    101. //出队
    102. Queue pop = queue[0];
    103. //后继数据前移
    104. for (int d = 0; d < qu_count - 1; d++) {
    105. queue[d] = queue[d + 1];
    106. }
    107. qu_count--;
    108. //比较找到最小值赋予到vl
    109. int min = vl[pop.next] - G.matrix[pop.prev][pop.next];
    110. if (min <= vl[pop.prev])
    111. vl[pop.prev] = min;
    112. //把当前出队的顶点前面相连的顶点入队
    113. for (int i = 0; i < G.vexnum; i++) {
    114. if (G.matrix[i][pop.prev] != 0 && G.matrix[i][pop.prev] != Maxint) {
    115. queue[qu_count].prev = i;
    116. queue[qu_count].next = pop.prev;
    117. qu_count++;
    118. }
    119. }
    120. }
    121. //03--处理e
    122. int e_count = 0;
    123. for (int i = 0; i < G.vexnum; i++) {
    124. for (int j = 0; j < G.vexnum; j++) {
    125. if (G.matrix[i][j] != 0 && G.matrix[i][j] != Maxint) {
    126. e[e_count].begin_index = i;
    127. e[e_count].e = ve[i];//套用公式
    128. e[e_count].end_index = j;
    129. e_count++;
    130. }
    131. }
    132. }
    133. //04--处理l
    134. int l_count = 0;
    135. for (int i = 0; i < G.vexnum; i++) {
    136. for (int j = 0; j < G.vexnum; j++) {
    137. if (G.matrix[i][j] != 0 && G.matrix[i][j] != Maxint) {
    138. //套用公式
    139. l[l_count++] = vl[j] - G.matrix[i][j];
    140. }
    141. }
    142. }
    143. //输出结果
    144. printf("\nve数组结果:");
    145. for (int j = 0; j < G.vexnum; j++) {
    146. printf("%d ", ve[j]);
    147. }
    148. printf("\nvl数组结果:");
    149. for (int j = 0; j < G.vexnum; j++) {
    150. printf("%d ", vl[j]);
    151. }
    152. printf("\ne的结果:");
    153. for (int j = 0; j < G.arcnum; j++) {
    154. printf("%d ", e[j].e);
    155. }
    156. printf("\nl的结果:");
    157. for (int j = 0; j < G.arcnum; j++) {
    158. printf("%d ", l[j]);
    159. }
    160. printf("\n关键路径路线如下:\n");
    161. for (int j = 0; j < G.arcnum; j++) {
    162. if (e[j].e - l[j] == 0) {
    163. int start = e[j].begin_index;
    164. int end = e[j].end_index;
    165. printf("%s->%s %d\n", G.vexs[start].id, G.vexs[end].id, G.matrix[start][end]);
    166. }
    167. }
    168. //释放空间
    169. free(ve);
    170. free(vl);
    171. free(e);
    172. free(l);
    173. free(queue);
    174. ve = vl = e = l = queue = NULL;
    175. }

    测试结果:

    以上就是本期的内容了,喜欢的话点个赞吧!

     分享一张壁纸:

  • 相关阅读:
    强化学习笔记
    抽象类与接口
    通俗易懂,一文学会前端缓存
    2022年下半年信息系统项目管理师下午真题及答案解析
    防火墙导致Linux发送网络报文出现errno等于1的错误码
    Selenium和Requests搭配使用
    系统架构技能之设计模式-组合模式
    SpringMVC
    shell脚本实现Mysql分库分表备份
    String类的常用方法(Java)
  • 原文地址:https://blog.csdn.net/m0_73633088/article/details/134080394