#include
using namespace std;
const int maxn = 1000;
struct Node{int v, w;}node; //终点编号,边权(活动时长)
vector G[maxn]; //邻接表存边,即存活动
int n,inDegree[maxn] = { 0 }; //点数,存每个点的入度
stack topOrder; //拓扑排序栈
int ve[maxn],vl[maxn]; //对结点来说,即事件是时间点,是最早开始时间,最迟结束时间(可以理解为休息时间区间)
//对这个点射出的边权来说,即活动的最早开始时间和最晚开始时间
vector pre[maxn]; //存每个点的前驱
bool topologicalSort(){ //拓扑排序,用G与inDegree去更新topOrder和ve
queue q; //开一个队列
int c = 0; //当前队列中的点数
for (int i = 0; i < n; i++) //逐个点遍历
if (inDegree[i] == 0)q.push(i);//入度为零的点入队
while (!q.empty()){ //队列非空
int u=q.front();q.pop();//读出队
c++; //入队点数加1
topOrder.push(u); //此点可以入排序栈
for (int i = 0; i < G[u].size(); i++){ //射出的边逐条遍历
int v = G[u][i].v, w = G[u][i].w; //读出终点与边权
inDegree[v]--; //此点入度减一
if(inDegree[v] == 0)q.push(v); //入度为0就压入这个点
if(ve[u]+w>ve[v])ve[v]=ve[u]+w; //距离松驰
}
}
return c==n?true:false; //排序排了C个点,不能成功把N个点排完就FALSE
}
vector path;
void DFS(int s,int e){ //传入起点与当前点,利用pre去构造path去输出
if(s==e){ //到达起点就进行以下输出并返回
path.push_back(s); //路径数组压入终点
for (int i=path.size()-1;i>=0;i--){ //逆向输出
printf("%d",path[i]); //输出当前点编号
if(i>0)printf("->"); //然后是箭头
else printf("\n"); //故意留个0来输出个换行而已
}
path.pop_back(); //删掉最后一个元素
return;
}//未到终点就下面的代码
path.push_back(e); //把当前点压入路径数组(是反向存的)
for(int i=0;i%d\n", u + 1, v + 1); //输出时加1以还原输入时做出的改变
pre[v + 1].push_back(u + 1); //射向点v(+1)的前驱就是u(+1),此句是为了下面输出路径用
}
}
}
printf("所有的关键路径如下所示:\n"); //提示语,下面开始输出关键路径(再深搜一次)
DFS(1, n); //深搜得到路径
return ve[n - 1]; //n-1号点最早开始时间是完成时间,最后一个点是最后一个活动结束这个事件的时间点
}
int main(){
//freopen("i.txt", "r", stdin); //键盘输入流重定向为i.txt文件输入流
int m,a,b,w; //边数,临时变量A,B,W
scanf("%d %d",&n,&m); //n为点数,m为边数
for (int i = 0; i < m; i++){ //逐条边输入
scanf("%d%d%d", &a, &b, &w); //a、b为事件,w为ab这条边所表示的活动耗费的时间
node.v = b-1; node.w = w; //结点存值方便下一句压入邻接表
G[a-1].push_back(node); //邻接表存图,为了方便a、b都减1
}
printf("关键路径长度为:%d\n",criticalPath()); //输出关键路径长度
return 0;
}
/*
输入为(即i.txt内容):
9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
*/
/*
简单来说有四步
第一步拓扑排序及求ve:确保无环,同时理新好topOrder栈及ve,即每个结点的最早开始休息时间,或者说是此结点射出的每个边权活动最早开始时间(若只求关键路径长度已可输出)
第二步更新vl,利用已更新好的汇点最早开始休息时间及topOrder栈倒序更新每一个点的vl,即每个结点的最晚结束休息时间,或者说是此结点射出的每个边权活动最晚开始时间
第三步求关键活动:边权活动最早开始时间e就是边起点ve,最迟结束时间l是边终点vl-边权w,若e==l就是关键活动,并记录pre即每个点的前驱
第四步求关键路径:利用pre进行深搜,开path记录当前深搜的序列,当搜到
*/