求AOE网的关键路径

以边表示活动,以顶点表示事件的有向网称为AOE(activity on edge)网.AOE网是一个
有向无环图,权值表示活动持续的时间。可以用AOE网来估计工程完成的时间。由于工程
只有一个开始点和一个完成点,所以在无环路的条件下,网中只有一个入度为0的点和一
个出度为0的点.
下面是几个和AOE网有关的概念:
(1)路径长度:路径上各个活动的持续时间之和

(2)完成工程的最短时间:由于AOE网中有活动是并行进行的,所以完成工程的最短时间
就是从开始点到完成点的最长路劲长度。
(3)活动最早开始时间(earlist time)(e(i)):从开始点到顶点vi的最长路径称为事件vi的最早发生时间,

这个时间决定了以vi为尾的弧表示的活动的最早开始时间.
(4)活动最晚开始时间(latest time)(l(i)):在不推迟整个工程完成的前提下,活动最迟开始的时间
(5)完成活动的时间余量:该活动的最迟开始时间减去最早开始时间
(6)关键路径(critical path):路径长度最长的路径称为关键路径
(7)关键活动(critical activity):关键路径上的活动称为关键活动,关键活动的特点是:e(i)=l(i)
分析关键路径的目的就是辨别在整个工程中哪些是关键活动,以便争取提高关键活动的工作
效率,缩短整个工程的工期。

文件"aoe.h"

#include<iostream>
#include<string>
#include<stack>
#include<iomanip>
using namespace std;

const int MAX_VEX_NUM=20;
int ve[20];//全局变量,存放各个事件的最早发生时间

class ArcNode //表结点
{
public:
	int adjvex;
	int info;//权值
	ArcNode *nextarc;
};

class VNode //头结点
{
public:
	string data;
	int indegree; //顶点的入度
	ArcNode *firstarc;
};

class ALGraph 
{
private:
	VNode vertices[MAX_VEX_NUM];
	int arcnum;
	int vexnum;
public:

	void Create_ALG()
	{
		//构造有向网		
		string v1,v2;
		int i,j,w;
		ArcNode *p=NULL;

		cout<<"输入顶点数和边数:";
		cin>>vexnum>>arcnum;

		cout<<"输入顶点名称:";
		for(i=0;i<vexnum;i++)
		{
			cin>>vertices[i].data;
			vertices[i].firstarc=NULL;
			vertices[i].indegree=0;
		}

		for(int k=0;k<arcnum;k++)
		{
			cout<<"按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:";
			cin>>v1>>v2>>w;
			i=Locate_Vex(v1);
			j=Locate_Vex(v2);

			while(i==-1 || j==-1)
			{
				cout<<"输入的顶点错误,重新输入:";
				cin>>v1>>v2;
				i=Locate_Vex(v1);
				j=Locate_Vex(v2);
			}

			p=new ArcNode;
			p->adjvex=j;
			p->info=w;
			p->nextarc=vertices[i].firstarc;
			vertices[i].firstarc=p;
			vertices[j].indegree+=1; //作为有向弧的头的顶点入度加1
		}

		cout<<"有向网构造完成"<<endl;
	}

	int Locate_Vex(string v) //求顶点在顶点数组中的位置
	{
		for(int k=0;k<vexnum && vertices[k].data!=v;k++);
		if(k<vexnum)
			return k;
		else
			return -1;
	}
								/*关键路径求解思想*/
			/*---------------------------------------------------------------
			/ 辨别关键路径就是要找出l(i)=e(i)的活动,为了求AOE网中的e(i)和l(i)
			/ 首先应该求出每个事件的最早发生时间ve(j)和最晚发生时间vl(j),如果
			/ 活动ai用弧<j,k>表示,那么持续时间记为dut(<j,k>).则有如下关系:
			/ e(i)=ve(j),   l(i)=vl(k)-dut(<j,k>);
			/ 求事件的vj的最早发生时间ve(j)和最迟发生时间vl(j)要分两步进行:
			/ (1):从ve(0)=0(假设顶点0是开始点)开始,根据下面公式计算其它事件
			/ 的最早开始时间: ve(j)=Max{ve(i)+dut(<i,j>)} 其中i是j的所有直接前驱的集合
			/ (2)从vl(n-1)=ve(n-1)开始,根据下面公式计算其他事件的最晚开始时间:
			/ vl(i)=Min{vl(j)-dut(<i,j>)} 其中j是i的直接后继的集合
			/ 上述两个公式必须分别在拓扑有序和逆拓扑有序的前提下进行,也就是说ve(j-1)
			/ 必须在vj全部直接前驱的最早发生时间都求得以后才能确定。而vl(j-1)则必须在
			/ vj的所有直接后继的最晚发生时间求得之后才能确定,因此可以在拓扑排序的基础
			/ 上计算所有事件的ve(j-1)和vl(j-1).
			/ 为了能按逆拓扑有序序列的顺序计算各个顶点的vl值,需记下在拓扑排序的过程中
			/ 求得的拓扑有序序列,这只需要增加多一个栈,用来存储拓扑有序序列即可.
			/ 由于栈的结构特点,拓扑有序序列出栈就变成逆拓扑有序序列了.
			/----------------------------------------------------------------*/

	//求所有事件的最早发生时间
	bool Topo_Order(stack<int> &T)
	{
		stack<int> s;
		ArcNode *p=NULL;
		for(int i=0;i<vexnum;i++)
			if(!vertices[i].indegree)
				s.push(i);
		int count=0;
		for(i=0;i<vexnum;i++)
			ve[i]=0; //设各顶点最早发生为0
		while(!s.empty())
		{
			int k=s.top();
			s.pop();
			T.push(k);
			count++;
			for(p=vertices[k].firstarc;p;p=p->nextarc)
			{
				int w=p->adjvex;
				if(vertices[w].indegree)
					vertices[w].indegree--;
				if(!vertices[w].indegree)
					s.push(w);
				if(ve[k]+p->info>ve[w]) //求ve[w]的最早发生时间
					ve[w]=ve[k]+p->info;
			}
		}
		if(count<vexnum)
			return 0;
		else
			return 1;
	}
	
	//求所有事件的最晚发生时间并求出关键活动和关键路径
	void Critical_Path()
	{
		stack<int> T;
		string cp[10];
		int c=0;
		if(!Topo_Order(T))
		{
			cout<<"该有向网有环!"<<endl;
			return;
		}
		int vl[20];
		for(int i=0;i<vexnum;i++)
			vl[i]=ve[vexnum-1]; //初始化顶点事件最迟发生时间
		
		while(!T.empty())
		{
			ArcNode *p=NULL;
			int j=T.top();
			T.pop();
			for(p=vertices[j].firstarc;p;p=p->nextarc)
			{
				int k=p->adjvex;
				int dut=p->info;
				if(vl[k]-dut<vl[j])
					vl[j]=vl[k]-dut;
			}
		}
		//下面是根据关键活动的特点(最早开始时间和最晚开始时间相等)求关键活动,做上标记
		cout<<"tail  head  weight  earliest time  latest time  tag "<<endl;
		for(int j=0;j<vexnum;j++)
			for(ArcNode *p=vertices[j].firstarc;p;p=p->nextarc)
			{
				int k=p->adjvex;
				int dut=p->info;
				int ee=ve[j];
				int el=vl[k]-dut;
				char tag;
				tag=(ee==el)?'*':' ';
				cout<<setw(3)<<vertices[j].data<<setw(6)<<vertices[k].data<<setw(7)<<dut<<setw(9)<<ee<<setw(16)<<el<<setw(9)<<tag<<endl;
				if(tag=='*' && j==0)
				{
					cp[c]=vertices[j].data;
					c++;
					cp[c]=vertices[k].data;
					c++;
				}
				else if(tag=='*' && j!=0)
				{
					cp[c]=vertices[k].data;
					c++;
				}			
			}
		cout<<"The critical activities are ended with * "<<endl;
		cout<<"So the critical path is :";
		for(i=0;i<c-1;i++)
			cout<<cp[i]<<"->";
		cout<<cp[c-1]<<endl;
	}

};

主函数"main.cpp"

#include"aoe.h"

int main()
{
	ALGraph G;
	G.Create_ALG();
	G.Critical_Path();
	cout<<endl;
	return 0;
}

输入和输出结果:

输入顶点数和边数:6 8
输入顶点名称:v1 v2 v3 v4 v5 v6
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v1 v2 3
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v1 v3 2
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v2 v5 3
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v2 v4 2
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v3 v4 4
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v3 v6 3
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v4 v6 2
按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v5 v6 1
有向网构造完成
tail  head  weight  earliest time  latest time  tag
 v1    v3      2        0               0        *
 v1    v2      3        0               1
 v2    v4      2        3               4
 v2    v5      3        3               4
 v3    v6      3        2               5
 v3    v4      4        2               2        *
 v4    v6      2        6               6        *
 v5    v6      1        6               7
The critical activities are ended with *
So the critical path is :v1->v3->v4->v6

Press any key to continue

根据输入所生成的有向网如下所示:

当关键路径只有一条时,输出关键路径是对的,当关键路径不知一条时就是错的,但是依然是可以找出所有的关键活动,路径输出的算法,以后会完善到可以输出所以关键路径

求逆拓扑有序序列 除了利用求拓扑有序时进栈外,还可以直接用DFS遍历该有向图,直到该结点的所有邻接结点都输出之后,才将其输出,这个序列就是逆拓扑有序序列

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AOE中,关键路径的步骤如下: 1. 首先,需要计算每个活动的最早开始时间ve(i)。从源点开始,逐个计算每个活动的最早开始时间,直到汇点。ve(i)的计算公式为:ve(i) = max{ve(j) + d(j,i)},其中j为活动i的前驱活动,d(j,i)为活动j到活动i的持续时间。 2. 接下来,需要计算每个活动的最迟开始时间vl(i)。从汇点开始,逐个计算每个活动的最迟开始时间,直到源点。vl(i)的计算公式为:vl(i) = min{vl(j) - d(i,j)},其中j为活动i的后继活动,d(i,j)为活动i到活动j的持续时间。 3. 然后,计算每个活动的最早完成时间e(i)。e(i)的计算公式为:e(i) = ve(i)。 4. 接着,计算每个活动的最迟完成时间l(i)。l(i)的计算公式为:l(i) = vl(i) - d(i),其中d(i)为活动i的持续时间。 5. 最后,计算每个活动的总浮动时间l(i) - e(i)。如果某个活动的总浮动时间为0,则该活动活动活动所在的路径即为关键路径。 需要注意的是,只有减少活动的时间才可能缩短工期,而且只有在不改变关键路径的前提下减少活动的时间才可能缩短工期。 #### 引用[.reference_title] - *1* [(数据结构)AOE关键路径](https://blog.csdn.net/weixin_51609435/article/details/123817811)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [图的关键路径(AOE络)](https://blog.csdn.net/m0_61433144/article/details/128730798)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值