介绍
关键路径是项目网络图中一系列代表最长任务序列的活动或任务,这些任务决定了项目的最短完成时间。任何关键路径上的活动的延迟都会导致整个项目的延迟。因此,项目管理人员会特别关注关键路径上的活动,以确保项目按时完成。
大体思路
- 理解AOE网络:
- AOE网络(Activity On Edges)是一个有向无环图(DAG),其中顶点表示事件(event),有向边表示活动(activity),边上的权值表示活动的持续时间。
- 计算最早发生时间(ve):
- 从起始节点开始,按照拓扑排序的顺序,计算每个节点的最早发生时间。对于每个节点,其最早发生时间为其所有前置节点(通过有向边指向该节点的节点)的最早发生时间中的最大值加上当前边的权值。
- 初始时,起始节点的最早发生时间设为0。
- 计算最迟发生时间(vl):
- 从终止节点开始,逆着拓扑排序的顺序,计算每个节点的最迟发生时间。对于每个节点,其最迟发生时间为其后继节点(由该节点通过有向边指向的节点)的最迟发生时间中的最小值减去当前边的权值。
- 初始时,终止节点的最迟发生时间等于其最早发生时间加上从该节点到终止节点的最长路径长度。
- 确定关键活动:
- 对于每条边(活动),如果其最早开始时间等于其最晚开始时间,则该活动为关键活动。
- 关键活动构成了关键路径,即从起始节点到终止节点具有最大路径长度的路径。
- 优化项目时间:
- 缩短关键路径上的关键活动时间可以缩短整个项目的完成时间。
- 如果有多条关键路径,需要同时缩短这些路径上的关键活动时间,以有效缩短整个项目的周期。
实现步骤
1.0结构体等定义
2.0 (拓扑排序)
目的是为了求活动实现路径,同时在其中顺便将vet(最早发生时间)数组求出。
3.0 核心算法
这个算法中,根据topo求出的路径,先初始化vlt(最晚发生时间)数组为抽象最大值,然后把vlt[vexnum-1]赋为vet[vexnum-1],这个必不可少,因为他是接下来逆拓扑排序求vlt数组的起点。求出vlt数组后,for循环遍历所有边,寻找余量为0活动(余量为0,也就对映关键活动。可以理解为现实中的容错时间,或者说“墨迹时间”)。eet最早开始时间,elt是最晚开始时间。两者相减即为余量。最后打印。
源代码
#include<stack> #include<iostream> #include<vector> using namespace std; const int MAX = 1e5+10; int vexnum,arcnum; //最早发生时间,最晚发生时间 int vet[MAX] = {0}, vlt[MAX]; struct EdgeNode{ int adjvex; int value; EdgeNode*nextarc; }; struct VexNode{ int indegree; EdgeNode*firstarc; }; VexNode Vex[MAX]; bool TopoSort(vector<int>&topo){ stack<int>stack; for(int i = 0;i < vexnum;i++){ if(Vex[i].indegree == 0){ stack.push(i); } } while(!stack.empty()){ int top = stack.top(); stack.pop(); topo.push_back(top); for(EdgeNode*p = Vex[top].firstarc;p!=nullptr;p = p->nextarc){ int end = p ->adjvex; if(!(--Vex[end].indegree)){ stack.push(end); } if(vet[top]+p->value > vet[end]){ vet[end] = vet[top]+p->value; } } } if(int(topo.size())<vexnum){ return false; } return true; } void CriticalPath(){ std::vector<int>topo; TopoSort(topo); for(int i=0;i<vexnum;i++){ vlt[i] = MAX;//********************* */ } // vlt[topo[0]] = 0;/*非必要,代码逻辑最后可以计算出来 */ //逆向求vlt值 vlt[vexnum-1] = vet[vexnum-1];//*************************** */ for(int i = topo.size()-2;i>=0;i--){ int pre = topo[i]; for(EdgeNode*p = Vex[pre].firstarc;p!=nullptr;p = p->nextarc){ int end = p->adjvex; if(vlt[end] - p->value <vlt[pre]){ vlt[pre] = vlt[end] - p->value; } } } std::cout << "The following is the critical path" << std::endl; for(int i = 0;i<vexnum;i++){///------------------------------- for(EdgeNode*p = Vex[i].firstarc;p!=nullptr;p = p->nextarc){ int end = p->adjvex; int eet = vet[i]; int elt = vlt[end] - p->value; if(eet == elt){ std::cout << "v"<<i<<"-->"<<"v"<<end<<"=="<<p->value<<std::endl; } } } } int main() { std::cout <<"please input the nodenum and arcnum : "; std::cin >> vexnum>>arcnum; for(int i = 0;i<arcnum;i++){ int x,y,z;//弧尾,弧头,权重 std::cin >> x>>y>>z; Vex[y].indegree++; EdgeNode*p = new EdgeNode; p->adjvex = y; p->value = z; p->nextarc = Vex[x].firstarc; Vex[x].firstarc = p; } CriticalPath(); // // 6 8 // 1 2 3 // 1 3 2 // 3 4 4 // 4 6 2 // 2 5 3 // 2 4 2 // 3 6 3 // 5 6 1 return 0; }