关键路径(Critical Path)是项目管理中的一个概念,它指的是项目中最长的任务序列,决定了完成项目所需的最短时间。关键路径上的任务称为关键任务,它们对项目的完成时间有直接影响。
我们需要先了解以下几个概念:
AOE网(Activity On Edge Network):是一种有向无环图,用于表示项目中的任务和任务之间的依赖关系。在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动需要的时间)。
(1)在AOE网中仅有一个入度为0的顶点,称为开始顶点(源点),它表示整个工程的开始;
(2)也仅有一个出度为0的顶点,称为结束顶点(汇点),它表示整个工程的结束。
(边的第一个标注是活动id,第二个是权值)

并且遵循以下规则:
1.只有到达某个事件后以该事件为起点的活动才能开始;
2.只有以某个事件为终点的所有活动结束后,该事件才能发生(要消除所有指向该事件的边)。
计算关键路径通常需要用到以下概念:
事件的最早发生时间ve,事件最迟发生时间vl,活动的最早发生时间ae,活动的最迟发生时间al,活动冗余时间d=al-ae。如果某活动的d=0,则该活动是关键活动。
关键路径求解步骤:
1.从源点(开始项目)开始,按拓扑顺序计算每个事件的最早发生时间ve;
2.从汇点(结束项目)开始,按拓扑逆序计算每个事件的最迟发生时间vl;
3.计算每个活动的最早发生时间ae和最迟发生时间al;
4.判断每个活动是否为关键活动,即判断d是否为0。
代码实现:
#include<bits/stdc++.h>
using namespace std;
struct Activity {
int id;
int u, v, w;//活动的起点和终点和权值
int ve; // 活动最早开始时间
int vl; // 活动最迟开始时间
int d; // 活动冗余时间
};
struct Event {
int id;
int ve; // 事件最早发生时间
int vl; // 事件最迟发生时间
vector<Activity> inEdges; // 以这个事件开始的活动
};
int n, m; // n表示顶点数,m表示边数
vector<Event> events; // 事件集合
void init() {
cin >> n >> m;
// 初始化事件集合
for(int i = 0; i < n; i++) {
events.push_back(Event{i, INT_MIN, INT_MAX, vector<Activity>()});
}
// 初始化活动集合
for(int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
events[u].inEdges.push_back(Activity{i, u, v, w, 0, INT_MAX, -1});
}
}
void dfs(int v, int currTime) {
events[v].ve = max(events[v].ve, currTime);//更新事件的最早发生时间
if(events[v].inEdges.size() == 0) return; // 结束活动
for(auto& activity : events[v].inEdges) {
dfs(activity.v, currTime + activity.w); // 递归计算后续活动的最早发生时间
}
}
void getStartEvent(int v, queue<int>& q) {
for(Event event : events) {
for(Activity edge : event.inEdges) {
if(edge.v == v) {
q.push(edge.u);
}
}
}
}
//bfs更新事件的最迟发生时间
void getVL(int v) {
queue<int> q;
q.push(v);
while(!q.empty()) {
int currV = q.front();
q.pop();
queue<int> tempq;
getStartEvent(currV, tempq);
while(!tempq.empty()) {
int Min = INT_MAX;
for(Activity &act : events[tempq.front()].inEdges) {
if(act.v == currV) {
act.vl = events[currV].vl - act.w;
Min = min(Min, act.vl);
}
}
events[tempq.front()].vl = Min;
q.push(tempq.front());
tempq.pop();
}
}
}
int main() {
//初始化一个AOE网
init();
//通过dfs来计算每个事件的最早发生时间
dfs(0, 0); //从源点开始
//更新活动的最早发生时间
for(int i = 0; i < n; i++) {
for(auto& activity : events[i].inEdges) {
activity.ve = events[i].ve;
}
}
events[n - 1].vl = events[n - 1].ve; //更新汇点的最迟发生时间
//通过拓扑排序来计算每个事件和活动的最迟发生时间
getVL(n-1);
//计算每个活动的冗余时间,输出关键路径
int spend = 0;
for(int i = 0; i < n; i++) {
for(auto& activity : events[i].inEdges) {
activity.d = activity.vl - activity.ve;
if(activity.d == 0) {
spend += activity.w;
cout << "活动id:" << activity.id << " 活动起始事件id:" << activity.u << " 活动指向事件id:" << activity.v << endl;
}
}
}
cout << "关键路径长度为:" << spend << endl;
return 0;
}
/*
4 4
0 2 2
0 1 1
1 2 3
2 3 2
*/
//图例就是这组数据
/*
6 8
0 1 3
0 2 2
1 3 2
1 4 3
2 3 5
2 5 3
3 5 2
4 5 1
*/
4037

被折叠的 条评论
为什么被折叠?



