美文网首页PTA甲级
拓扑排序(删边/DFS)➕AOE网关键路径

拓扑排序(删边/DFS)➕AOE网关键路径

作者: zilla | 来源:发表于2019-07-22 16:06 被阅读0次

这篇博客的缘起是女鹅问的一道AOE网的题,然而上一波数据结构并没有全看完就莫名搁置了,当时看图的后半部分算法很多,想着实现一下,一直静不下心搞,就无限期搁置了(叹气,戴起老花镜开始研究= =

题目

原题
牛客网上此题的讨论
在这个讨论区看到了五花八门的答案,陷入沉思,决定自己实现一下AOE网求关键活动。

动手实现

  • 首先看看这个算法详细的流程


    维基百科对AOE网的介绍
  • 拓扑排序有删边、dfs两种实现,下面实现的是删边法(🚩dfs有空再补吧
    严格按照算法的步骤实现如下
#include <cstdio>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <algorithm>

#define MAXN 1001 // max num of vertices
#define INF 0x3f3f3f3f
using namespace std;
vector<pair<int, int >> graph[MAXN], rgraph[MAXN];
int nv, ne; // vertex 1 - nv  edge from----weight----to
int in_degree[MAXN] = {0}; // out_degree[i] = graph[i].size();
queue<int> mq; // TopologicalSort
stack<int> ms; //reverse TopologicalSort
int ve[MAXN], vl[MAXN];
map<pair<int, int>, pair<int, int>> ee_el;

// 合法输入为 无重边的有向无环图
void bulidAOE() {
    scanf("%d%d", &nv, &ne);
    int from, to, weight;
    for (int i = 0; i < ne; ++i) {
        scanf("%d%d%d", &from, &to, &weight);
        graph[from].emplace_back(to, weight);
        rgraph[to].emplace_back(from, weight);
        in_degree[to]++;
        ee_el[make_pair(from, to)] = make_pair(0, -weight);
    }
    puts("Activity On Edge Network has been built.");
    for (int i = 1; i <= nv; ++i) {
        for (auto item:graph[i]) {
            printf("V%d ---- %d ---> V%d\n", i, item.second, item.first);
        }
    }
};

/*
 * 删边法
 * queue 保存拓扑排序结果
 * 循环:选择入度为0的结点,将编号入队,删去与之相连的边(该点发出的边, 将指向的结点入度-1即可)
 * 若过程中某步找不到入度为0的结点,return false
 */
bool topologicalSort() { // if not DAG return false
    printf("TopologicalSort\n");
    bool done[MAXN] = {false};
    for (int i = 0; i < nv; ++i) {
        int curr = -1;
        for (int j = 1; j <= nv; ++j) {
            if (!done[j] && in_degree[j] == 0) {
                curr = j;
                break;
            }
        }
        if (curr == -1) {
            puts("Error! Not a DAG!");
            return false;
        }
        mq.push(curr);
        done[curr] = true;
        printf("%d - ", curr);
        for (auto item:graph[curr]) {
            in_degree[item.first]--;
        }
    }
    puts("");
    return true;
}

/*
 * 此处需要知道各结点的入边,因此最初存边的时候,不仅要存from-to,还要存to-from
 * 需要取max min 注意初始化
 * 若用邻接矩阵实现,直接遍历一列就知道有无入边了
 * 计算结点事件最早时间 ve【i】
 *      按拓扑序(queue出队)的次序,计算事件最早时间
 *      顺便将出队的结点序号入栈,以获得逆拓扑序列
 * 计算结点时间最晚时间 vl【i】
 *      按逆拓扑序(stack出栈)的次序,计算事件最晚开始时间
 *
 * 活动记作 pair<from,to>    map<pair<int, int>, pair<int, int>> ee_el;
 * 计算活动最早开始时间 ee
 * 计算活动最晚开始时间 el
 */
void calcEarlyLate() {
    fill(ve + 1, ve + nv + 1, 0);
    fill(vl + 1, vl + nv + 1, INF);
    //  calc ve
    int curr;
    if (!mq.empty()) {
        curr = mq.front();
        mq.pop();
        ms.push(curr);
        ve[curr] = 0;
    }
    while (!mq.empty()) {
        curr = mq.front();
        ms.push(curr);
        mq.pop();
        for (auto item:rgraph[curr]) {
            ve[curr] = max(ve[item.first] + item.second, ve[curr]);
        }
    }
    // calc vl
    vl[curr] = ve[curr]; // curr = ms.top()
    ms.pop();
    while (!ms.empty()) {
        curr = ms.top();
        ms.pop();
        for (auto item:graph[curr]) {
            vl[curr] = min(vl[item.first] - item.second, vl[curr]);
        }
    }
    printf("No        ve        vl\n");
    for (int i = 1; i <= nv; ++i) {
        printf("%d%10d%10d\n", i, ve[i], vl[i]);
    }
    printf("from      to        ee        el        \n");
    // calc ee and el
    for (auto &item : ee_el) {
        item.second.first = ve[item.first.first];// ee[j,k] = ve[j]
        item.second.second += vl[item.first.second]; // el[j,k] = vl[k] - weight<j-k>
        printf("%d%10d%10d%10d", item.first.first, item.first.second, item.second.first, item.second.second);
        puts(item.second.first == item.second.second ? "        Critical" : "");
    }
}

int main() {
    bulidAOE();
    if (topologicalSort()) {
        calcEarlyLate();
    }
    return 0;
}
  • 把上面那道题目丢进去跑一下:


    运行结果

    ⚠️ 于是发现牛客讨论区列出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满足同时缩短三条关键路径,即缩短了最长路径 #

至此,终于清爽的八达鸟~

相关文章

  • 拓扑排序(删边/DFS)➕AOE网关键路径

    这篇博客的缘起是女鹅问的一道AOE网的题,然而上一波数据结构并没有全看完就莫名搁置了,当时看图的后半部分算法很多,...

  • 拓扑排序(删边/DFS)➕AOE网关键路径

    这篇博客的缘起是女鹅问的一道AOE网的题,然而上一波数据结构并没有全看完就莫名搁置了,当时看图的后半部分算法很多,...

  • 剑指 Offer II 113. 课程顺序

    拓扑排序 bfs dfs

  • 利用DFS实现拓扑排序

    拓扑排序定义利用“DAG必有零入度顶点”的特性,实现拓扑排序基于DFS搜索的拓扑排序 1. 拓扑排序定义 将一个有...

  • 有向图(二)

    拓扑排序: 一幅有向图的拓扑排序即为所有顶点的逆后序排列。证明: 对于任意边v->w,在调用dfs(v)时,只会是...

  • 拓扑排序+关键路径

    拓扑排序 有向无环图DAG顶点表示活动的网络AOV网:用DAG图表示一个工程,其顶点表示活动,有向边...

  • 图-关键路径(AOE网)

    AOE网:在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向...

  • 拓扑排序和关键路径求值

    拓扑排序和关键路径的求值都是对图的应用,严格来说其实是对有向图应用。 我们先来描述一下拓扑排序,拓扑排序就是根据路...

  • 图的最短路径和拓扑排序

    拓扑排序 2、图的最短路径 3、图的拓扑排序

  • 684. 冗余连接

    主要掌握并查集/dfs/拓扑排序.dfs里要注意从后面开始查,特别是dfs函数如何设计以及

网友评论

    本文标题:拓扑排序(删边/DFS)➕AOE网关键路径

    本文链接:https://www.haomeiwen.com/subject/uqoilctx.html