图——基本的图算法(四)关键路径 - Go语言中文社区

图——基本的图算法(四)关键路径


图——基本的图算法(四)关键路径

1. 基本概念

(1)AOV网(Activity On Vertex Network)
AOV网是一个表示工程的有向图中,其中的顶点用来表示活动,弧则用来表示活动之间的优先关系。举个简单的例子,假定起床后可以边煮水,边刷牙洗脸,但洗脸要在刷牙后,煮好水,刷好牙洗好脸以后,就可以喝水了,这个活动的AOV图如下(其中的每个顶点都表示一个活动,而顶点之间的弧表示了活动之间的执行先后顺序):
在这里插入图片描述

(2)AOE网( Activity On Edge Network)
AOE网是一个表示工程的带权有向图,其中的顶点用来表示某个事件,弧用来表示活动,弧上的权值用来表示活动持续的时间。例如上述例子的AOE网如下:
在这里插入图片描述

(3)AOE网和AOV网的区别
AOV网:其顶点用来表示活动。AOE网是用来表示活动之间的制约关系。
AOE网:顶点表示事件,边表示活动,边上的权值用来表示活动持续的时间。AOV网是用来分析工程至少需要花多少时间完成,或是为了缩短时间需要加快哪些活动等问题。

(4)源点、汇点、路径长度
在AOE网中,
始点源点:入度为0的顶点,它表示一个工程的开始;
终点汇点:出度为0的顶点,它表示一个工程的结束;
路径长度:路径上各个活动所持续的时间之和。

(5)关键路径、关键活动
在AOE网中,从源点到汇点具有最大长度的路径称为关键路径,在关键路径上的活动称为关键活动

2. 关键路径算法

2.1 基本思想

(1)要找出一个AOE网中的关键路径,就要先找出网里的关键活动,这些关键活动间的路径就是关键路径。

(2)判断一个活动是不是关键活动,方法是要先找到所有活动的最早开始时间和最晚开始时间,并且对它们进行比较,如果二者相等(意味着这个活动在该工程中没有时间松动),说明这个活动是关键活动。

(3)对于活动<Vi, Vj>,它最早的开始时间等于事件Vi最早的发生时间earlist[Vi](事件v的最早发生时间用earlist[v])。假设E[Vi]表示所有到达顶点Vi的弧的集合,len<Vi, Vj>表示活动<Vi, Vj>的持续时间,那么:
在这里插入图片描述
注意,这里假设顶点下标从0开始,所以Vi==0,则表示它是源点,因此最早的开始时间为0;当某个顶点不是源点,那么只有在它前面的事件都发生完后,才能轮到这个事件,所以用了max。

(4)对于活动<Vi, Vj>,它最晚的开始时间等于事件Vj最晚的发生时间减去这个活动的持续事件,即latest[Vj]-len<Vi, Vj>(事件v的最晚的发生时间用latest[v])。假设S[Vj]表示所有从顶点Vj出发的弧的集合,len<Vj, Vk>表示活动<Vj, Vk>的持续时间,那么:
在这里插入图片描述

2.2 算法实现

(1)数据结构

typedef struct EdgeListNode{ //边表结点
    int adjId;
    int weight;
    EdgeListNode* next;
};

typedef struct VertexListNode{ //顶点表结点
    int in; //入度
    int data;
    EdgeListNode* firstadj; //指向其边表
};

typedef struct GraphAdjList{ //图结构
    int vertexnumber; //顶点个数
    int edgenumber;
    VertexListNode vertextlist[Maximum];
};

(2)具体实现
1)由基本思路可以知道,要求一个AOE网的关键路径,步骤如下:
A. 首先初始化每个顶点的最早开始为0,然后对AOE网进行拓扑排序,在排序的过程中获取每个顶点的最早开始时间;
B. 获取拓扑排序后,初始化每个顶点的最晚开始时间为汇点的最早开始时间,并从AOE网的汇点开始,从后往前,对每个顶点找到求其最晚开始时间;
C. 遍历图中的每条边(方法是遍历图中每个顶点的边表),求其最早开始时间和最晚开始时间,如果二者相等,则这是个关键活动,将其加入关键路径中。

2)代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<vector>
#include<sstream>
#include<list>
#include<stdlib.h>
#include<queue>
using namespace std;

#define Maximum 1000

typedef struct EdgeListNode{
    int adjId;
    int weight;
    EdgeListNode* next;
};

typedef struct VertexListNode{
    int in;
    int data;
    EdgeListNode* firstadj;
};

typedef struct GraphAdjList{
    int vertexnumber;
    int edgenumber;
    VertexListNode vertextlist[Maximum];
};

//拓扑排序,返回拓扑排序结果,earlist存储每个顶点的最早开始时间
vector<int> ToplogicalSort(GraphAdjList g, int *earlist) {
    vector<int>v; //存储拓扑排序结果
    queue<int>q; //存储入度为0的顶点
    EdgeListNode *temp;
    int i, j, k, ans;

    ans = 0;
    v.clear();
    while(!q.empty()) {
        q.pop();
    }

	//找出所有入度为0的顶点
    for(i=1; i<=g.vertexnumber; i++) {
        if(g.vertextlist[i].in == 0) {
            q.push(i);
        }
    }

    while(!q.empty()) {
        i = q.front();
        v.push_back(i);
        q.pop();
        ans++;
        temp = g.vertextlist[i].firstadj;
        while(temp != NULL) {
            k = earlist[i] + temp->weight;
            if(k > earlist[temp->adjId]) {
                earlist[temp->adjId] = k;
            }
            j = --g.vertextlist[temp->adjId].in;
            if(j == 0) {
                q.push(temp->adjId);
            }
            temp = temp->next;
        }
    }

    if(ans < g.vertexnumber) {
        v.clear();
    }
    return v;
}

//求关键路径,返回存储关键路径顶点的vector
vector<int> CriticalPath(GraphAdjList g) {
    vector<int>ans;
    vector<int>path;
    int i, j, k, *earlist, *latest;
    EdgeListNode *temp;

	//earlist存储每个顶点的最早开始时间
	//latest存储每个顶点的最晚开始时间
    earlist = (int*)malloc(sizeof(int) * (g.vertexnumber+1));
    latest = (int*)malloc(sizeof(int) * (g.vertexnumber+1));
    path.clear();
    for(i=1; i<=g.vertexnumber; i++) {
        earlist[i] = 0;
    }

    ans = ToplogicalSort(g, earlist);
    for(i=1; i<=g.vertexnumber; i++) {
        latest[i] = earlist[ans[g.vertexnumber-1]];
    }
    for(i=g.vertexnumber; i>0; i--) {
        temp = g.vertextlist[i].firstadj;
        while(temp!=NULL) {
            if(latest[temp->adjId] - temp->weight < latest[i]) {
                latest[i] = latest[temp->adjId] - temp->weight;
            }
            temp = temp->next;
        }
    }

	//遍历每条边
    for(i=1; i<=g.vertexnumber; i++) {
        temp = g.vertextlist[i].firstadj;
        while(temp != NULL) {
            j = earlist[i];
            k = latest[temp->adjId] - temp->weight;
            if(j == k) { //是关键活动
       			//把该活动的两个顶点加入path
                path.push_back(i);
                path.push_back(temp->adjId);
            }
            temp = temp->next;
        }
    }
    return path;
}

(3)测试
在这里插入图片描述

int main() {
    GraphAdjList g;
    EdgeListNode *e;
    int i, j, k;

    g.vertexnumber = 5;
    g.edgenumber = 7;


    for(i=1; i<=g.vertexnumber; i++) {
        g.vertextlist[i].data = i;
        g.vertextlist[i].firstadj = NULL;
    }

    g.vertextlist[1].in = 0;
    g.vertextlist[2].in = 1;
    g.vertextlist[3].in = 2;
    g.vertextlist[4].in = 2;
    g.vertextlist[5].in = 1;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 2; e->weight = 2; e->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = e;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 4; e->weight = 1; e->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = e;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 5; e->weight = 1; e->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = e;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 3; e->weight = 3; e->next = g.vertextlist[2].firstadj; g.vertextlist[2].firstadj = e;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 4; e->weight = 5; e->next = g.vertextlist[2].firstadj; g.vertextlist[2].firstadj = e;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 3; e->weight = 8; e->next = g.vertextlist[4].firstadj; g.vertextlist[4].firstadj = e;

    e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
    e->adjId = 3; e->weight = 1; e->next = g.vertextlist[5].firstadj; g.vertextlist[5].firstadj = e;

    vector<int>ans;
    ans = CriticalPath(g);

    for(i=0; i<ans.size(); i+=2) {
        cout<<ans[i]<<"->"<<ans[i+1]<<endl;
    }
    cout<<endl;

    return 0;
}
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/hh66__66hh/article/details/83418423
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2023-01-03 15:46:50
  • 阅读 ( 70 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢