关键路径

#include <iostream>
#include <climits> /* for INT_MAX */
#include <stack>
#include <algorithm>
using namespace std;
/** 顶点数的最大值*/
const int MAX_NV = 100;
/** 边的权值,对无权图,用0 或1 表示是否相邻;对有权图,则为权值. */
typedef int graph_weight_t;
const graph_weight_t GRAPH_INF = INT_MAX;
/**
*@struct 邻接矩阵.
*/
struct graph_t {
    int nv; /* 顶点数*/
    int ne; /* 边数*/
    /* 邻接矩阵,存放边的信息,如权重等*/
    graph_weight_t matrix[MAX_NV][MAX_NV];
};
graph_t g;
/** 拓扑排序的结果. */
int topological[MAX_NV];
/** 关键路径,其余顶点为-1. */
int path[MAX_NV];
/*
* @brief 按照拓扑排序的顺序,计算所有顶点的最早发生时间ve.
* @param[in] g 图对象的指针
* @param[out] topological 保存拓扑排序的结果
* @param[out] ve 所有事件的最早发生时间
* @return 无环返回true,有环返回false
*/
static bool toposort_ve(const graph_t &g, int topological[],
    graph_weight_t ve[]) {
        const int n = g.nv;
        /* in_degree[i] 是顶点i 的入度*/
        int *in_degree = new int[n]();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (g.matrix[i][j] < GRAPH_INF)
                    in_degree[j]++;
            }
        }
        stack<int> s;
        for(int i = 0; i < n; i ++) {
            if(in_degree[i] == 0) {
                s.push(i);
            }
        }
        fill(ve, ve + n, 0);
        int count = 0; // 拓扑序列的元素个数
        while(!s.empty()) {
            // 删除顶点u
            const int u = s.top(); s.pop();
            topological[count++] = u;
            --in_degree[u]; // 变成-1,表示已经输出
            // 更新入度
            for (int i = 0; i < n; i++) if (g.matrix[u][i] < GRAPH_INF) {
                --in_degree[i];
            }
            // 更新邻接点的ve
            for (int i = 0; i < n; i++) if (g.matrix[u][i] < GRAPH_INF) {
                if (ve[i] < ve[u] + g.matrix[u][i])
                    ve[i] = ve[u] + g.matrix[u][i];
            }
            // 选择入度为0 的顶点
            for (int i = 0; i < n; i++) if (g.matrix[u][i] < GRAPH_INF) {
                if (in_degree[i] == 0) s.push(i);
            }
        }
        delete[] in_degree;
        if(count < n) return false; // 有环
        else return true; // 无环
}
/**
* @brief 求关键路径,第一个顶点为起点,最后一个顶点为终点.
* @param[in] g 图对象的指针
* @param[out] ve 所有事件的最早发生时间
* @param[inout] path 关键路径
* @return 无环返回关键路径的顶点个数,有环返回0
*/
int critical_path(const graph_t &g, int path[MAX_NV]) {
    int count = 0; // 关键路径的顶点个数
    graph_weight_t *ve = new graph_weight_t[g.nv];
    graph_weight_t *vl = new graph_weight_t[g.nv];
    if (!toposort_ve(g, topological, ve)) return 0; // 有环
    for (int i = 0; i < MAX_NV; i++) path[i] = -1;
    // 初始化vl 为最大
    for (int i = 0; i < g.nv; i++) vl[i] = ve[g.nv-1];
    // 逆序计算vl
    for (int i = g.nv-1; i >=0; i--) {
        const int k = topological[i];
        for (int j = 0; j < g.nv; j++) {
            if (g.matrix[j][k] < GRAPH_INF) {
                if (vl[j] > vl[k] - g.matrix[j][k])
                    vl[j] = vl[k] - g.matrix[j][k];
            }
        }
    }
    for (int i = 0; i < g.nv; i++) {
        for (int j = 0; j < g.nv; j++) {
            const int e = ve[i];
            const int l = vl[j] - g.matrix[i][j];
            if (e == l) {
                if (i == 0) {
                    path[count++] = i;
                    path[count++] = j;
                } else {
                    path[count++] = j;
                }
            }
        }
    }
    delete[] ve;
    delete[] vl;
    return count;
}
/** 读取输入,构建图. */
void read_graph() {
    cin>> g.nv >> g.ne;
    // 初始化图,所有节点间距离为无穷大
    for (int i = 0; i < g.nv; i++)
        for (int j = 0; j < g.nv; j++)
            g.matrix[i][j] = GRAPH_INF;
    for (int k = 0; k < g.ne; k++) { // 读取边信息
        char chx, chy;
        graph_weight_t w;
        cin >> chx >> chy >> w;
        g.matrix[chx - 'A'][chy - 'A'] = w;
    }
}

int main() {
    read_graph();
    /* 关键路径*/
    const int count = critical_path(g, path);
    for (int i = 0; i < count; i++) {
        cout << (char)('A' + path[i]) << " ";
    }
    return 0;
}

/* test
输入数据:
6 8
A B 3
A C 2
C D 4
B D 2
C F 3
B E 3
E F 1
D F 2
输出: A C D F
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值