开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
算法简介
关键路径算法是一种用来解决 事件最短时间问题 的算法
现实案例:
小莹周末想跟对象出去玩,但是老师留了一堆作业,所以她在出去玩之前要先将作业完成,写完作业之后立马去约会肯定是不行滴,她还需要化妆,然后再美美地约对象去玩,然后等玩到深夜,回来还要卸妆,洗澡,洗衣服,如果玩的太晚,这些事情可能就会耽搁她睡觉。于是她得出结论, 是时候该分手了。。。。
由上可见,小莹在出去玩之前需要完成一些前置事件,之后才能进行 跟对象出去玩 这个事件,因此
跟对象出去玩 这个事件的 最早发生时间 取决于所有前置事件的完成时间。 然后,小莹在回家后为了不推迟睡觉时间,她就需要在睡前把各种杂务事做完,所以 杂务事件 的 最晚发生时间 是睡觉时间往前减去做完杂务所需的时间。
那么 关键路径就是 最早发生时间等于最晚发生时间的路径。 在这条路径上的活动集合,我们不能拖延,一旦这些活动的前置事件完成,我们就要立马去做这些事,否则就会耽误这个活动集合后面的活动。
在生活中,每件事的限定时间一般都会超出我们需要花的时间很多, 也就是说,当我们做这件事之前可以
拖延一段时间准备一段时间。所以经常就会有人在时间截止时间(DDL)前不久才将事情做完。 在日常生活中,适当增加关键路径活动,可以提高我们的工作效率。
算法分析
在关键路径算法中我们使用的图是------ AOE(Activity On Edge)网:活动在边上,边有权重链接.
算法思路
在这个算法中我们先求出每个事件的最早发生时间和最晚发生时间,然后再从起点开始遍历整张图,查看哪些点的最早发生时间等于最晚发生时间,这些点连接成的路径就是关键路径。
算法步骤
求解事件最早发生时间
对图做拓扑排序,接着将起点事件的最早发生时间设置为0。然后按照拓扑正序,我们从起点出发,设当前点为u,u的后置事件为v。
v的最早发生事件为(v的最早发生时间 与 u点的最早发生时间加上u事件的消耗时间 )取最大值,遵循了 事件的 最早发生时间 取决于所有前置事件的完成时间。代码如下:
for (int j = bian[0][x]; j; j = e[j].last) {
int y = e[j].to;
early[y] = max(early[y], early[x] + e[j].d);
}
求解事件最晚发生时间
我们将终点的最晚发生时间设置为终点的最早发生时间。然后按照拓扑反序,我们从汇点(终点)出发,设当前点为u,u的前置事件为v。
v的最晚发生时间等于 (v的最晚发生时间 与 u点最晚发生时间减去该事件的消耗时间)取最小值,遵循了 事件的 最晚发生时间 取决于后置事件的完成时间- 后置事件消耗的时间。代码如下
for (int j = bian[1][x]; j; j = e[j].last) {
int y = e[j].to;
last[y] = min(last[y], last[x] - e[j].d);
}
最后我们用dfs从起点遍历图,寻找最早发生时间等于最晚发生时间的点,他们连接起来的路径就是关键路径啦
详细代码:
代码默认起点为0,终点为n-1
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int n = 2333;
int n, m, u, v, d, deg[n], early[n], last[n],bian[2][n],cnt,top[n];
vector<int>path;
vector<vector<int>>ans;
struct edge {
int to, last, d;
}e[n<<1];
void addd(int u, int v, int d,int bian[]) {
e[++cnt].d = d;
e[cnt].to = v;
e[cnt].last = bian[u];
bian[u] = cnt;
}
void topsort() {
int head = 0, tail = 0;
top[0] = 0;
while (head <= tail) {
int t = top[head++];
for (int i = bian[0][t]; i; i = e[i].last) {
int j = e[i].to;
if (--deg[j] == 0)
top[++tail] = j;
}
}
}
void critical_path() {
topsort();
for (int i = 0; i <= n; i++) {//队列遍历顺序
int x = top[i];
for (int j = bian[0][x]; j; j = e[j].last) {
int y = e[j].to;
early[y] = max(early[y], early[x] + e[j].d);
}
}
memset(last, 0x3f, sizeof(last));
last[n] = early[n];
for (int i = n; i >= 0; i--) {
int x = top[i];
for (int j = bian[1][x]; j; j = e[j].last) {
int y = e[j].to;
last[y] = min(last[y], last[x] - e[j].d);
}
}
}
void dfs(int u) {
path.push_back(u);
if (!bian[0][u])ans.push_back(path);
for (int i = bian[0][u]; i; i = e[i].last) {
int y = e[i].to;
if(early[y]==last[y])
dfs(y);
}path.pop_back();
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> u >> v >> d, addd(u, v, d, bian[0]), addd(v, u,d, bian[1]),deg[v]++;
critical_path();
dfs(0);
printf("关键路径为\n");
for (auto &t : ans) {
for (int i = 0; i < t.size(); i++)
cout << t[i] << ' ';
cout << endl;
}
return 0;
}