/*
严格参照《数据结构(严蔚敏版)》
2017.12.6
by kk
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<cmath>
#include<algorithm>
#define infinity 0x3f3f3f3f
#define maxsize 100
#define OK 1
#define ERROR 0
using namespace std;
typedef int VertexType ;
typedef struct NODE//边结点的定义
{
VertexType adjvex;//结点
int weight ;//此弧的权值
NODE *next;
} Node;
typedef struct//邻接表头的定义
{
int in;//用来记录顶点的入度
VertexType data;
NODE *first;
} nodeList;
typedef struct
{
int num_vex,num_edge;//结点个数,边的条数
VertexType vex[maxsize];//结点集
nodeList headvex[maxsize];//邻接表
} GraphL;
void create(GraphL &g)//邻接表的创建
{
int i,j,k;
int x,y,z;
NODE *e;
scanf("%d%d",&g.num_vex,&g.num_edge);
for(i=0; i<g.num_vex; i++)//邻接表的初始化
{
scanf("%d",&g.headvex[i].data);
g.headvex[i].in=0;//每个结点的入度为0
g.headvex[i].first=NULL;//邻接表头的next为空
}
for(i=0; i<g.num_edge; i++)
{
scanf("%d%d%d",&x,&y,&z);
e=(NODE*)malloc(sizeof(NODE));
e->weight=z;
e->adjvex=y;
//建立的是有向图
e->next=g.headvex[x].first;//头插法插入新的结点
g.headvex[x].first=e;
g.headvex[y].in++;
}
}
int stack2[maxsize],top2;
int *etv,*ltv;//分别是储存结点事件最早和最晚开始的时间的数组-
int TopologicalOrder(GraphL g)
{
int stack1[maxsize],top1=0;
top2=0;
//stack1是拓扑排序的时候用,用来储存入度为0的结点下标
//stack2是寻找顶点时间最晚开始时间(ltv)的时候用,其实就是将按拓扑排序时遍历的结点的次序塞到stack2里
/*
也就是说求ltv时遍历结点的顺序刚好跟求etv时遍历结点的顺序相反
拓扑排序时遍历结点的顺序为正,ltv时的遍历结点的顺序为反
*/
int i,j,Count=0,k;
etv=(int *)malloc(g.num_vex*sizeof(int ));
ltv=(int *)malloc(g.num_vex*sizeof(int ));
if(etv==NULL||ltv==NULL)return ERROR;
memset(etv,0,g.num_vex*sizeof(int));
memset(ltv,0,g.num_vex*sizeof(int));
memset(stack1,0,sizeof(stack1));
memset(stack2,0,sizeof(stack2));
/*
!!!!!!!!!!!!!!!!!!!!!!!!!!
这里最重要(不是关于此算法的),如果知道C的这个知识点的请跳过。
memset()函数注意事项:
1.memset(a,0,sizeof(a));
memset(a,-1,sizeof(a));
以上两个当a是字符数组或者int数组都好使
2.memset(a,-1,sizeof(a));如果a是int数组不好使
3. memset(a,0,sizeof(a))当a是指针的时候就得注意了,若果是用malloc函数分配空间后的,
memset(a,0,sizeof(a))只会初始化a这个空间内的值为0,但并不会连同malloc函数分配的空间全都初始化为0
!!!
故正确的写法是memset(a,0,a分配空间的个数*sizeof(a))或者 memset(a,0,a分配的个数*sizeof(int))
*/
//先将入度为0的结点入栈
for(i=0; i<g.num_vex; i++)
if(g.headvex[i].in==0)stack1[++top1]=i;
//
// for(i=0; i<g.num_vex; i++)
// printf("%d ",etv[i]);
// puts("");
int gettop;
NODE *e;
while(top1!=0)
{
gettop=stack1[top1--];//出栈
stack2[++top2]=gettop;
++Count;
//如果遍历遍历结束后发现count的值小于结点的总个数,说明图中存在环
for(e=g.headvex[gettop].first; e; e=e->next)
{
k=e->adjvex;
if((--g.headvex[k].in)==0)//更新结点的入度
{
stack1[++top1]=k;//将结点入度为0的点继续入栈
}
if((etv[gettop]+e->weight)>etv[k])
{
etv[k]=etv[gettop]+e->weight;//修改结点事件最早发生时间
}
}
}
if(Count<g.num_vex)return ERROR;
else return OK;
}
void criticalPath(GraphL g)
{
NODE *e;
int i,j;
TopologicalOrder(g);
for(i=0; i<g.num_vex; i++)
printf("%d ",etv[i]);
puts("");
for(i=0; i<g.num_vex; i++)//初始化ltv全都为活动结束的那个时间
ltv[i]=etv[g.num_vex-1];
int gettop,k;//下面还是倒着遍历结点,找ltv数组的值
while(top2!=0)
{
gettop=stack2[top2--];
for(e=g.headvex[gettop].first; e; e=e->next)
{
k=e->adjvex;
if((ltv[k]-e->weight)<ltv[gettop])//k是弧头,gettop是弧尾(也就是说这条边是从gettop指向k的)
ltv[gettop]=ltv[k]-e->weight;
}
}
//找结点事件最早开始时间etv是找最大值,找结点事件最晚发生时间ltv是找最小值
int ete,lte;//分别代表弧最早开始时间和最晚开始时间
for(j=0; j<g.num_vex; j++)
{
for(e=g.headvex[j].first; e; e=e->next)
{
k=e->adjvex;
ete=etv[j];
lte=ltv[k]-e->weight;//边的三元组是(j,k,e->weight)
if(ete==lte)
{
printf("%d--%d %d\n",g.headvex[j].data,g.headvex[k].data,e->weight);
}
}
}
}
int main()
{
GraphL G;
create(G);
criticalPath(G);
return 0;
}
/*
10 13
0 1 2 3 4 5 6 7 8 9
0 1 3
0 2 4
1 3 5
2 3 8
2 5 7
1 4 6
3 4 3
4 6 9
4 7 4
5 7 6
6 9 2
7 8 5
8 9 3
*/