PTA甲级

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

2019-07-22  本文已影响0人  zilla

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

题目

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

动手实现

#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;
}

正解

回到wikipedia上对整体工期的解释,它应当是源点到汇点最长路径的长度,所以要缩短工期,就是要减少最长路径(关键路径)的长度

原题-图片
源点到汇点的路径&总长度:

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

上一篇下一篇

猜你喜欢

热点阅读