#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
*/