• [算法应用]关键路径算法的简单应用


    (0)免责声明:

    算法和数据结构都是我自己写的,为了提交这次的作业,所以把去年写过的算法重新翻新了一下,数据结构也简单整理了一下,重新放上来了.

    整个程序是可以顺利跑通的,同学们可以用代码检测一下自己的结果对不对,切勿抄袭.

    (真的不想手推关键路径了....真的....不过复习整个算法,还有重新编写数据结构确实花了点时间)

    (1)问题描述

    很简单,就是要est和lst,还有关键路径

    (在下面的代码中,我们习惯性地称呼为etv和ltv)

    数据分析,一共14条边,10个点,按照顺序从0开始算起,按照起点,终点,权重三要素,可以把数据划分为

    1. 0 1 3
    2. 0 2 6
    3. 0 3 10
    4. 1 4 13
    5. 4 6 2
    6. 2 5 4
    7. 3 5 8
    8. 5 6 3
    9. 5 7 20
    10. 5 8 4
    11. 8 9 12
    12. 7 9 10
    13. 8 7 1
    14. 6 9 7

    (2)代码原理分析/算法逻辑

    代码的实现逻辑是这样的

    1.首先按照拓扑结构进行的,按照拓扑顺序遍历每个点,如点A,到下流点B这一路径上,如果B的最早开始时间早于A的最早时间加上A-B需要的时间,那么就把B的最早开始时间延后.

    2.然后按照逆拓扑结构进行,如果A的最晚开始时间+A->B需要的时间晚于B的最晚开始时间,那么就把A的最晚开始时间往前移动

    3.关键路径就是,A的最早开始时间+AB需要的时间=B的最晚开始时间,那么这条就是关键路径之一

    (3)数据存储结构的实现

    1. //图结构已经储存完毕;(graph为邻接表进行存储)
    2. class Node {
    3. public:
    4. int num;
    5. int weight; //路径的消耗时间保存在终点上,视为路径徐娅萍的时间
    6. Node* next;
    7. Node(int a) { num = a; };
    8. };
    9. class headNode {
    10. public:
    11. int length = 0;
    12. Node* point=NULL;
    13. int in, out = 0;
    14. };
    15. class graph {
    16. public:
    17. headNode arr[20];
    18. int NodeNum, EdgeNum = 0;
    19. };
    20. void createGraph(graph& Graph) {
    21. int edge, point,start,end,weight;
    22. cout << "请输入边数 和 点数" << endl;
    23. cin >> edge >> point;
    24. Graph.EdgeNum = edge;
    25. Graph.NodeNum = point;
    26. for (int i = 0; i < point; i++) {
    27. Graph.arr[i] = headNode();
    28. }
    29. cout << "请按照顺序输入起点,终点,权重" << endl;
    30. for (int i = 1; i <= edge; i++) {
    31. cin >> start >> end >> weight;
    32. Graph.arr[start].out++;//出度+1
    33. Graph.arr[end].in++;//入度+1
    34. Node* temp = new Node(end);
    35. temp->weight = weight;//赋予权重
    36. temp->next = Graph.arr[start].point;
    37. Graph.arr[start].point = temp;//头插法插入
    38. }
    39. }
    40. //图结构已经储存完毕;

    (4)完整代码

    1. //关键路径算法
    2. //受到学妹的启发,终于明白这个最短是什么意思了
    3. //最短并非指的是从起点到终点的最短距离(事实上这个也能求,不过理论上不行)
    4. //关键路径的时间加起来,其实是完成所有任务需要的最短时间(默认任务是可以并发完成的)
    5. //感谢来自隔壁实验室的22级同学的讨论和提示
    6. int etv[20];//最早开始时间
    7. int ltv[20];//最晚开始时间
    8. int top = 0;//指针
    9. int Stack[20];//这是储存用到的栈,或者说是数组
    10. //关键路径算法的实现
    11. void topuGraph(graph& Graph) {
    12. stack<int> stack2;
    13. for (int i = 0; i < Graph.NodeNum; i++) {
    14. if (Graph.arr[i].in == 0)stack2.push(i); //先把入度为0的点压入栈中
    15. etv[i] = 0; //设置每个点的最早开始时间为0
    16. }
    17. while (!stack2.empty()) {
    18. int temp = stack2.top(); //获取一个出发点,并且弹出来,然后按照拓扑排序,保存一个信息
    19. stack2.pop();
    20. Stack[top++] = temp;
    21. Node* point = Graph.arr[temp].point; //然后开始对点的所有下游点进行遍历
    22. while (point) {
    23. if (--Graph.arr[point->num].in == 0) { //先处理这个点作为入度的情况,也就是对下面的下游点们剔除这个点的影响
    24. stack2.push(point->num);
    25. }
    26. if (etv[point->num]weight) {//修改最早数值 : 如果这个点的最早开始时间,早于上一个任务的开始时间+完成需要时间
    27. etv[point->num] = etv[temp] + point->weight; //就适当延后这个点的开始时间
    28. }
    29. point = point->next;
    30. }
    31. }
    32. }
    33. void keyRoad(graph& Graph) {
    34. for (int i = 0; i < Graph.NodeNum; i++) {
    35. ltv[i] = etv[Graph.NodeNum-1]; //给每个点都设置一个最晚的.或者足够大的最晚开始时间
    36. }
    37. while (top >= 0) { //拓扑排序从后往前(按照之前的顺序)
    38. int temp = Stack[top--];
    39. Node* point = Graph.arr[temp].point; //同样是遍历这个点所有的子点
    40. while (point) { //如果最晚开始时间晚于子点的最晚开始时间-子点需要完成的时间,那么就把最晚开始时间往前调
    41. if (ltv[temp] > ltv[point->num] - point->weight) {
    42. ltv[temp] = ltv[point->num] - point->weight;
    43. }
    44. point = point->next;
    45. }
    46. }
    47. //在邻接表中寻找关键路径,
    48. //如果一个点的最早开始时间+这个点需要的时间恰好为下一个点开始的最早时间,那么这个路径就一定是关键路径了
    49. int ete, lte = 0;
    50. for (int i = 0; i < Graph.NodeNum; i++) {
    51. Node* point = Graph.arr[i].point;
    52. while (point) {
    53. ete = etv[i];
    54. lte = ltv[point->num]-point->weight;
    55. if (ete == lte) {
    56. cout<<"关键路径为"<
    57. cout << (char)(i+65) << "--->" << (char)(point->num+65) <<"("<weight<<")" << endl;
    58. }
    59. point = point->next;
    60. }
    61. }
    62. }
    63. int main(){
    64. graph g;
    65. createGraph(g);
    66. topuGraph(g);
    67. keyRoad(g);
    68. cout<<"每个点的最早,最晚开始时间分别为:"<
    69. for (int i = 0; i < g.NodeNum; ++i) {
    70. cout<<(char)(i+65)<<" 最早开始时间为:"<", 最晚开始时间为:"<
    71. }
    72. }

    (5)跑通的最后结果

    关键路径为
    A--->D(10)
    关键路径为
    D--->F(8)
    关键路径为
    F--->H(20)
    关键路径为
    H--->J(10)
    每个点的最早,最晚开始时间分别为:
    A  最早开始时间为:0, 最晚开始时间为:0
    B  最早开始时间为:3, 最晚开始时间为:26
    C  最早开始时间为:6, 最晚开始时间为:14
    D  最早开始时间为:10, 最晚开始时间为:10
    E  最早开始时间为:16, 最晚开始时间为:39
    F  最早开始时间为:18, 最晚开始时间为:18
    G  最早开始时间为:21, 最晚开始时间为:41
    H  最早开始时间为:38, 最晚开始时间为:38
    I  最早开始时间为:22, 最晚开始时间为:36
    J  最早开始时间为:48, 最晚开始时间为:48

  • 相关阅读:
    Docker安装Gitlab-ruuner
    java项目之线上教学平台(springboot框架)
    SpringBoot整合Mybatis(使用c3p0数据源)
    Power BI DAX 编写利器 —— DaxStudio 的简单用法
    在Linux下如何实现禁止运行该程序多次?
    深入了解MySQL中的JSON_ARRAYAGG和JSON_OBJECT函数
    6个机器学习可解释性框架
    C++查漏补缺与新标准(C++20,C++17,C++11)02 C++快速回顾(二)
    springboot集成redisson
    解密数仓的SQL ON ANYWHERE技术
  • 原文地址:https://blog.csdn.net/weixin_62697030/article/details/133612448