为什么要找关键路径
拓扑排序:以某种偏序关系得出一个集合的全序
在拓扑排序中,把当前的网看成AOV网,活动都在结点,有向边表示先后依赖关系
实现:还是用栈或者队列实现,只要前驱变成0,进栈,如果栈都空了,但是还没找到所有的结点,说明有循环依赖,工程有死锁。
//利用拓扑路径法求有向图的关键路径//
#include<bits/stdc++.h>
using namespace std;
typedef int Status;
typedef int VertexType;
typedef char InfoType ;
typedef struct ArcNode//弧
{
int adjvex;//值
struct ArcNode* nextarc;//下一个
InfoType *info;
int weight;//完成工作的时间
}ArcNode;
typedef struct VNode//结点或者单个数组元素
{
VertexType data;
ArcNode *firstarc;//邻接表单个元素下的后继是以弧的形式存在的
}VNode,*AdjList;
typedef struct //邻接表
{
AdjList vertices;
int vexnum,arcnum;
}ALGraph;
//邻接表的创建
Status CreatDN(ALGraph &G)
{
cin>>G.vexnum>>G.arcnum;
for(int i=1;i<=G.vexnum;i++)//输入每个结点并且每个结点的后继都是空
{
cin>>G.vertices[i].data;
G.vertices[i].firstarc=NULL;
}
for(int i=0;i<G.arcnum;i++)//输入弧
{
char v1,v2;
int w,i,j;
cin>>v1>>v2>>w;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
ArcNode *arc=(ArcNode*)malloc(sizeof(ArcNode));//因为是个指针所以要malloc一下
arc->weight=w;//弧的权值
arc->adjvex=j;//出度为j
arc->nextarc=G.vertices[i]->firstarc;
G.vertices[i].firstarc=arc;
}
return OK;
}
//用栈实现拓扑排序的思想(部分函数没有实现)//这个函数实现与本题无关
Status TopologicalSort(ALGraph G)
{
int *InDegree;
InDegree=(int*)malloc(sizeof(int)*G.vexnum);
GetInDegree(G,InDegree);//求每个结点的的入度个数
InitStack(S);//初始化这个栈
for(int i=1;i<=G.vexnum;i++)//找到第一个入度为0的点作为排序起点
{
if(!InDegree[i]) Push(S,i);
}
int count=0;
while(!StackEmpty(S))//只要栈不为空
{
int num;
Pop(S,num);//出来一个结点
OutPutElem(G.vertices[num].data);//输出出来
count++;//只要在栈里面输出一个点就加加
for(ArcNode p=G.vertices[num];p!=NULL;p=p->nextarc)
{//将目前结点的出度结点的入度都减一,入度为0的结点再入栈
InDegree[p->adjvex]--;
if(!InDegree[p->adjvex]) Push(S,p->adjvex);
}
}
if(count<G.vexnum)return ERROR:
else return OK;
}//T=O(n+e)
//对拓扑排序前期求所有结点的入度
void GetInDegree(ALGraph G,int &InDegree)
{
memset(InDegree,0,sizeof(InDegree));
for(int i=1;i<=G.vexnum;i++)
{
ArcNode* p=G.vertices[i].firstarc;
while(p)
{
InDegree[p->adjvex]++;
p=p->nextarc;
}
}
}
//拓扑排序并且保存从起点到每个结点的最长路的长度
Status GetVEandRvsTopoOrder(ALGraph &G,Stack &T)
{
int *InDegree;
int *vl;
vl=(int*)malloc(sizeof(int)*G.vexnum);
InDegree=(int*)malloc(sizeof(int)*G.vexnum);
memset(vl,0,sizeof(vl));
memset(InDegree,sizeof(InDegree));
GetInDegree(G,InDegree);
InitStack(S);
InitStack(T);//用来存逆拓扑排序
for(int i=1;i<=G.vexnum;i++)//找到所有的入度为0的结点而不是只找一个入度为0的点
{
if(InDegree[i]==0)
Push(S,i);
}
int count=0;
while(!StackEmpty(S))//只要栈不为0
{
int num;
Pop(S,num);
Push(T,num);
count++;
for(ArcNode*p=G.vertices[num].firstarc;p!=NULL;p=p->nextarc)
{
InDegree[p->adjvex]--;//把点和它连接的箭头删掉
if(!InDegree[p->adjvex])Push(S,p->adjvex);//只要出现入度为0,入栈
if(vl[num]+p->weight>vl[p->adjvex])
{
vl[p->adjvex]=vl[num]+p->weight;//数组存的就是从起点到
//当前结点的最长路
}
}
}
if(count<G.vexnum)return ERROR;
else return OK;
}
//求关键点
Status CriticalPath(ALGraph G)
{
InitStack(T);
if(GetVEandRvsTopoOrder(G,T))return ERROR;//进行拓扑排序
int *vl=(int*)malloc(sizeof(int)*(G.vexnum+1));//用来存最晚激活时间
for(int i=1;i<=G.vexnum;i++)//
vl[i]=ve[G.vexnum];//每个点的最晚开始时间
//求一个结点的最晚开始时间,意思是保证工期的前提之下,从最晚工期往前推
//当一个点的后继有多个的时候,为了保证后继结点都能完成,此节点的最晚开始时间,要找减之后的最小值
int j;
Pop(S,j);
ArcNode* p=G.vertices[j].firstarc;
while(p)
{
if(vl[j]>vl[p->adjvex]-p->weight)//存最小的
vl[j]=vl[p->adjvex]-p->weight;
}
for(int i=1;i<=G.vexnum;i++)
{
ArcNode *q=G.vertices[i].firstarc;
while(q)
{
int ee=ve[i];//最早激活时间
int el=vl[q->adjvex]-p->weight;//最晚激活时间
if(ee=el)//如果相等则为关键点
cout<<i<<q->adjvex<<"是关键点"<<endl;
}
}
}
当AOV图看
1.进行拓扑排序,同时求出到每个结点的最长路径,最后一个结点的ve[]值就是最长路径就是最早激活时间。就是最短工期
2.找vl[],从后往前找,在栈T里面一个一个地出,这个结点的最晚激活时间就是它所有后继减去权值最小值,(因为如果有两个或者多个后继,要保证在最短工期完成后面的所有工作,所以尽量选早的)
3.最后求弧AOE图,弧ab的最早激活时间就是a的最早激活时间,最晚激活时间就是b-弧的权值。
4.只要ee==el这就是最短路径