这篇博客的缘起是女鹅问的一道AOE网的题,然而上一波数据结构并没有全看完就莫名搁置了,当时看图的后半部分算法很多,想着实现一下,一直静不下心搞,就无限期搁置了(叹气,戴起老花镜开始研究= =
题目
牛客网上此题的讨论
在这个讨论区看到了五花八门的答案,陷入沉思,决定自己实现一下AOE网求关键活动。
动手实现
-
首先看看这个算法详细的流程
step1 拓扑排序
有删边、dfs两种实现,下面实现的是删边法(dfs有空再补吧
0923填坑= =
dfs求拓扑序思路
在dfs时打时间戳,某点的时间戳表示的是此点dfs结束(返回)的时间。
按时间戳从大到小,即可得到拓扑序列。
若dfs过程中重复访问到某个点,则有环,不存在合法的拓扑序列。
e.g. 1011 | 万妖穴
#include
#include
using namespace std;
bool graph[1001][1001] = {false}, in_seq[1001] = {false};
int in_deg[1001] = {0}, nn, mm, h1, h2;
vector seq;
int toposort() {
int size = 0;
for (int j = 0; j < nn; ++j) {
int curr = -1;
for (int i = 0; i < nn; i++) {
if (!in_seq[i] && in_deg[i] == 0) {
curr = i;
seq.push_back(curr);
size++;
in_seq[curr] = true;
for (int k = 0; k < nn; ++k) {
if (graph[curr][k]) in_deg[k]--;
}
break;
}
}
if (curr == -1) return size;
}
return nn;
}
int main() {
scanf("%d%d", &nn, &mm);
for (int i = 0; i < mm; ++i) {
scanf("%d%d", &h1, &h2);
graph[h1][h2] = true;
in_deg[h2]++;
}
int cnts = toposort();
if (cnts == nn) {
puts("YES");
for (int i = 0; i < nn; ++i) {
printf("%d", seq[i]);
printf(i < nn - 1 ? " " : "\n");
}
} else {
puts("NO");
printf("%d\n", nn - cnts);
}
return 0;
}
关键路径,严格按照算法的步骤实现如下
#include
#include
#include
#include
#include
-
把上面那道题目丢进去跑一下:
求关键路径
dfs(源点),到汇点就无路可走return了,可求出源点到汇点的所有路径。
若路径长度等于汇点的开始时间,则是关键路径。
或者,dfs时,若edge curr---item的最早开始时间=最晚开始时间,则继续dfs(item),否则return。这样求出的源点到汇点的路径即所有的关键路径。
critical path可以用vector< vector < int > >来存。
️于是发现牛客讨论区列出ve,vl,ee,el表格的朋友答案似乎不大靠谱???
似乎没一个列出正确的结果???
那么这道题到底怎么才能得到正解呢???
正解
回到wikipedia上对整体工期的解释,它应当是源点到汇点最长路径的长度,所以要缩短工期,就是要减少最长路径(关键路径)的长度。
源点到汇点的路径&总长度:
-
1 —— 2 —— 4 —— 6
18 -
1 —— 2 —— 5 —— 6
18 -
1 —— 3 —— 5 —— 6
27 -
1 —— 3 —— 2 —— 4 —— 6
27 -
1 —— 3 —— 2 —— 5 —— 6
27
后面三条为关键路径,只需找出能同时缩短他们三个的边即可。
其实答案有很多种,因为除了1——2都是关键活动 = =
缩短1——3可以缩短全部三条,缩短3——2可以缩短后两条,缩短5——6可以缩短第一和第三条(省略blabla)
看选项,发现只有C满足同时缩短三条关键路径,即缩短了最长路径 #