关键路径。

一、概念

事件:瞬时间/刹那间发生的一件事

活动:一个完整的流程,可能由很多事件组成。

AOV网:在DAG(无环有向图)中,用顶点表示的活动,用无权有向边表示活动之间的次序。

-----> 工程中小项目的次序

AOE网(Acitivity On Edge):在DAG中,用顶点表示的事件,用带权有向边表示活动,边权表示活动持续的时间。

----->工程中小项目的次序,每个小项目多久做完,整个工程多久做完(比AOV更详细)

        其中V1 V2 V3.......V9 表示事件, a1.........a11 表示活动,活动的取值表示完成该活动所需要的 时间,如a1 = 6 表示完成活动 a1 所需要的时间为 6 天。此外,每⼀事件 Vi 表示在它之前的活动已经完成, 在它之后的活动可以开始,如V5 表示活动 a4 a5 已经完成,活动 a7 a8 可以开始了。

二、整个工程多久做完?

AOE网中 有些活动是可以并行进行的,从起点事件到终点事件 有多条路 径,并且每条路径的长度是不一样的,当所有路径上的活动都完成时 才标 志着整个工程的完成,那么整个工程所需的时间:最长的路径的长度。那么就把最长的路径称为关键路径。关键路径上的活动称为关键活动。
--->关键路径不唯一 我们需要关注所有的关键路径

拿到一个AOE网时,需要找到所有的关键路径。----> 如何在AOE网中找到关键路径?
关键路径:从起点到终点,最长的路径(们),由关键活动组成的
关键活动:不能延期。 最早的开始时间==最晚开始时间
非关键活动:最早的开始时间 != 最晚开始时间

三、如何计算关键路径                                                                                                                                            

(1)事件的最早发生时间( ETV , Earlieast Time Of Vertex):
起点事件start的最早发生时间:ETV[s]=0;
其他事件的最早发生时间 基于topo序列去算 ETV[j]=max(ETV[j],ETV[i]+w)
(2)事件的最晚发生时间( LTV , Latest Time Of Vertex):
对于终点事件endl最早发生和最晚发生是同一个时间
终点事件end的最晚发生时间 LTV[e]=ETV[e]
倒着算 基于逆topo序列 LTV[i]=min(LTV[i],LTV[j]-w)

(3)活动的最早开始时间(ETE):这个活动最早开始做的时间。

等于该边 起点事件的ETV(最早发生时间); 
ETE= ETV[i]
(4)活动的最晚开始时间( LTE ):这个活动最晚可以可以开始做的时间, 如果晚于这个时间 就会延期
保证终点事件 DDL之前( LTV)按时发生
LTE=该边终点事件的LTV(最晚发生时间)-该边权值
LTE=LTV[j]-w
(5)关键活动 LTE==ETE--->关键路径

四、代码

#include <stdio.h>
#include <stdlib.h>
#define INF 65535
//链栈
typedef struct stackNode
{
	int data;//存放入栈节点的下标
	struct stackNode *next; 	
}snode,*stack; 
//初始化栈
stack inits()
{
	stack s=(snode*)malloc(sizeof(snode));
	s->next=NULL;
	return s;
 } 
//入栈函数:
stack ppush(stack s,int e)
{
	snode* p=(snode*)malloc(sizeof(snode));
	p->data=e;
	p->next=s->next;
	s->next=p;
	return s;
 } 
//出栈-->栈顶数据返回 
stack ppop(stack s,int *e)
{
	if(s->next==NULL)
	{
	   (*e)=-1;
	   return s;
	 } 
	snode* p=s->next;
	(*e)=p->data;
	s->next=p->next;
	p->next=NULL;
	free(p);
	p=NULL;
	
	return s;
 } 
//------------------------
//邻接表存带权有向图-----> AOE网 
//边单链表节点结构
typedef struct edgeNode
{
	int data;//存放入栈节点的下标
	struct edgeNode *next;
	int w;//1.边权	
}enode; 
//顶点数组
typedef struct vNode
{
	char d;
	struct edgeNode *first; 	
}Graph;
Graph g[105];
int n,m;
int ind[105];//入度 
int etv[105];//事件的最早发生时间
int ltv[105];//事件的最晚发生时间
//char topo[105];//保存拓扑序列 
int topo[105];//保存拓扑序列----基于顶点下标 
int find(char x)
{
	for(int i=0;i<n;i++)
	{
		if(g[i].d==x)
		{
			return i;
		}
	}
}
void toposort()
{
	stack s=inits();//初始化栈 
	for(int i=0;i<n;i++)
	{//初始化etv数组  
		etv[i]=0;
	 } 
	
	for(int i=0;i<n;i++)
	{
		if(ind[i]==0)
		{
			s=ppush(s,i);
		}
	}
	
    int e;//出栈的点的下标
	int k=0;//topo数组的下标; 
	int flag=0;
	for(int i=1;i<=n;i++)
	{
		s=ppop(s,&e);
		
		topo[k]=e;//出栈的顶点的下标 保存在topo序列里面 
		k++;
		enode* p=g[e].first;
		int j;
		while(p!=NULL)
		{
			j=p->data;//j就是e的出边邻接点  e---->j 
			if(etv[j]<(etv[e]+p->w))
			{//求etv 
				etv[j]=etv[e]+p->w;
			}
			ind[j]--;
			if(ind[j]==0)
			{
				s=ppush(s,j);
			}
			p=p->next;
		}	
	}	
}
void CriticalPath()
{
	int end=topo[n-1];//0 ~~ n-1
	for(int i=0;i<n;i++)
	{
//初始化ltv,将每个点的最晚发生时间都初始化成终点的最早发生时间即可 
		ltv[i]=etv[end];
	}
	int x;
	//逆拓扑序 更新ltv 
	for(int i=n-2;i>=0;i--)
	{
		x=topo[i];//x点的最晚发生时间
		enode* p=g[x].first;
		while(p!=NULL)
		{
			int j=p->data;//x-->j
			if(ltv[x]>(ltv[j]-p->w))
			{
				ltv[x]=ltv[j]-p->w;
			}
			p=p->next;
		}
		
	 } 
	printf("以下是关键活动:\n");
	//遍历所有的边,求每个边的ete lte
	int ete,lte;
	for(int i=0;i<n;i++)
	{
		for(enode* p=g[i].first;p!=NULL;p=p->next)
		{
			int j=p->data;///i--->j 
			ete=etv[i];
			lte=ltv[j]-p->w;
			if(ete==lte)
			{
				printf("%c %c\n",g[i].d,g[j].d);
			}
		}	
	} 
	
}
int main()
{

	scanf("%d %d",&n,&m);
	for(int i=0;i<n;i++)
	{	
		getchar();
		scanf("%c",&g[i].d);
		g[i].first=NULL;
	}
	char x,y;
	int xi,yi,wi;
	for(int i=0;i<m;i++)
	{
		getchar();
		scanf("%c %c %d",&x,&y,&wi);
		xi=find(x);
		yi=find(y);
		//统计入度
		ind[yi]++;
		enode* p=(enode*)malloc(sizeof(enode));
		p->data=yi;
		p->w=wi;//
		p->next=g[xi].first;
		g[xi].first=p;	
	}
	
	toposort(); 
	CriticalPath();
	
	return 0;
} 
/*
9 11
A B C D E F G H I
A B 6
A C 4
A D 5
B E 1
C E 1
D F 2
E G 9
E H 7
F H 4
G I 2
H I 4
*/

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值