求AOE-网的关键路径并打印


AOE-网就是用弧表示活动,是一种带权重的有向无环图,其中,顶点表示事件,弧表示活动,权表示活动持续的时间。AOE网在工程时间估算上有较广泛应用。如右图

                                                           

由于AOE网中有些活动可以并行进行,故完成工程的最短时间是从开始点到完成点的最长路径的长度。

这里我们要特别注意节点与弧的区别和联系。节点是瞬时事件,而弧是要持续一段时间的。节点的最早开始时间就是相应弧的最早开始时间。为了求得活动的最早与最迟开始时间,用ee,el表示,我们需要先求出其开始节点的最早,最迟开始时间,用数组ve[ ] ,vl[ ]表示。而ee==el的活动即为关键活动,关键路径就是有关键活动组成,他们从开始点到完成点,长度最长的路径。只有提前完成关键活动,才能缩短整个工期。

求ve[ ],vl[ ]:


第一步是从前往后推:

第二步是从后往前推,完成点是转折点,也就是说vl[完成点]==ve[完成点]。所谓的前后不仅以开始点、完成点为标识,而且指拓扑有序和拓扑逆序。为了得到拓扑逆序我们用一个栈来记录求拓扑有序,这样从栈顶到栈底就是逆序了。

求得ve[ ],vl[ ]后,就可以求各个活动的最早、最迟开始时间。

                                            

若ee(i)==el(i),我们把他记录下来,一遍打印全部的关键活动,和后面求关键路径。

下面是源码。首先是Graph.h文件,记录了构造有向图的数据结构,采用邻接表来存储,求得是首图的关键路径。

#ifndef _Graph_H
#define _Graph_H

#define MAX_VERTEX_NUM 20
#define InfoType double
#define VertexType int

typedef struct ArcNode {
	int adjvex;
	struct ArcNode *nextarc;
	InfoType info;
}ArcNode;

typedef struct VNode {
	VertexType data;
	ArcNode *firstarc;
}VNode,AdjList[MAX_VERTEX_NUM];

typedef struct {
	AdjList vertices;
	int vexnum, arcnum;
	int kind;
}ALGraph;

//全局变量
ALGraph *graph;

#endif
下面是CriticalPath.cpp文件。因为可能存在多条关键路径,用到了回溯法。

#pragma warning(disable:4996)

#include"Graph.h"
#include<iostream>
#include<stdlib.h>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<utility>
#include<stdio.h>

using namespace std;

vector<double>ve;//存储各顶点事件的最早开始时间
vector<double>vl;//存储最迟开始时间

map<string,pair<double,double>>Act;//存储各活动的最早和最迟开始时间
stack<int>T;//存储逆序
char buf[3 * MAX_VERTEX_NUM];//存储最长关键路径
vector<int>TopOrder;//存储拓扑序列
map<int, vector<int>>mp;//存储关键活动
int flag = 0;//节点是否存储信息的开关

//图中各节点从0编号
ALGraph * Establish_Graph() {
	int k;
	int arcnum;
	char  c = 'y';
	ALGraph * graph = (ALGraph *)malloc(sizeof(ALGraph));
	graph->kind = 1;
	cout << "输入图的顶点个数与弧的个数" << endl;
	cin >> graph->vexnum;
	cin >> graph->arcnum;
	
	for (int i = 0;i < graph->vexnum;i++) {
		if (flag == 0)
			graph->vertices[i].data = i;
		else
			cin >> graph->vertices[i].data;
		graph->vertices[i].firstarc = NULL;
		cout << "\t" << "输入顶点"<<i<<"的出弧de头部顶点、弧信息(权重)" << endl;
		//构建弧顶点
		cout << "该顶点出弧的个数" << endl;
		cin >> arcnum;
		k = 0;
		while (k<arcnum) {
			cout << "第" << k << "条弧" << endl;
			ArcNode *arc = (ArcNode*)malloc(sizeof(ArcNode));
			cin >> arc->adjvex;
			cin >> arc->info;
			arc->nextarc = graph->vertices[i].firstarc;//把弧插到头部
			graph->vertices[i].firstarc = arc;
			k++;
		}
	}
	return graph;
}

void free(ALGraph *G) {
	ArcNode *ptr,*temp;
	for (int i = 0;i < G->vexnum;i++) {
		ptr = G->vertices[i].firstarc;
		while (ptr) {
			temp = ptr->nextarc;
			delete ptr;
			ptr = temp;
		}
	}
	delete[] &G->vertices;
}
//求图中各顶点的入度函数
int * Indegree(ALGraph *graph) {
	ArcNode *arcptr;
	
	int *indegree = (int *)malloc(sizeof(int)*graph->vexnum);
	for (int i = 0;i < graph->vexnum;i++)
		indegree[i] = 0;
	for (int i = 0;i < graph->vexnum;i++) {
		for (arcptr = graph->vertices[i].firstarc;arcptr != NULL;arcptr=arcptr->nextarc) {
			indegree[arcptr->adjvex]++;
		}
	}
	return indegree;
}
//T记录拓扑逆序
int TopologicalOrder(ALGraph *G, stack<int> &T) {
	stack<int>S;
	ArcNode *ptr;

	int *degree;
	int count = 0;
	int v,k;

	for (int i = 0;i < G->vexnum;i++)
		ve.push_back(0);

	degree = Indegree(G);
	for (int i = 0;i < G->vexnum;i++)
		if (degree[i] == 0)
			S.push(i);
	while (!S.empty()) {
		v = S.top();
		T.push(v);
		TopOrder.push_back(v);
		S.pop();
		++count;
		for (ptr = G->vertices[v].firstarc;ptr;ptr = ptr->nextarc) {
			k = ptr->adjvex;
			if (--degree[k] == 0)
				S.push(k);
			if (ve[v] + ptr->info > ve[k])
				ve[k] = ve[v] + ptr->info;
		}
	}
	delete[] degree;

	if (count < G->vexnum)
		return -1;
	else
		return 0;
}
void show(vector<int> &arr) {
	char buf[3 * MAX_VERTEX_NUM];
	char* ptr=buf;
	int len;
	vector<int>::iterator pr= arr.begin();

	while (pr !=arr.end()) {
		if ((len = snprintf(ptr, 5, "%d->", *pr)) < 0)
			cout << "snprintf error:" << strerror(errno) << endl;
		ptr =ptr+len;
		pr++;
	}
	snprintf(ptr, 5, "END\n\0");
	cout << buf << endl;
}
//回溯算法打印所有可能的关键路径
void show_critical(int vexnum) {
	int i = 1;//第0层为原点
	vector<int> k{0,0};//头两个元素初始化为0
	vector<int> x{ 0 };//记录关键路径上各个节点
	int it, flag = 0;
	//k[i]记录第i层元素在set中的index

	while (i >= 1) {
		while (k[i] < mp.find(x[i - 1])->second.size()) {
			it=*((mp.find(x[i-1])->second.begin())+k[i]);
			x.push_back(it);//x[i]
			if (x[i] == vexnum - 1){
					flag = 1;
					show(x);
					break;
			}
			else if (mp.find(x[i]) != mp.end()) {
				i = i + 1;
				k.push_back(0);//k[i] = 0;
			}
			else {
				x.pop_back();
				k[i] = k[i] + 1;
			}
		}
		if (flag == 1) {
			x.pop_back();
			flag = 0;
		}
		i--;
		x.pop_back();
		k[i] += 1;
	}
}

int  CriticalPath(ALGraph *G) {
	double ee;//记录各弧上活动的最早开始时间
	double el;//活动最迟开始时间
	ArcNode *ptr;
	int j, k;
	int len;
	vector<int>st;
	map<int, vector<int>>::iterator mptr;
	
	char str[10];

	if (TopologicalOrder(G, T)<0)
		return -1;
	for (int i = 0;i < G->vexnum;i++)
		vl.push_back(ve[G->vexnum-1]);
	while (!T.empty()) {
		for (j = T.top(),ptr = G->vertices[j].firstarc;ptr;ptr = ptr->nextarc) {
			k = ptr->adjvex;
			if (vl[k] - ptr->info < vl[j])
				vl[j] = vl[k] - ptr->info;
		}
		T.pop();
	}
	for (j = 0;j < G->vexnum;j++) {
		for (ptr = G->vertices[j].firstarc;ptr;ptr = ptr->nextarc) {
			k = ptr->adjvex;
			ee = ve[j];
			el = vl[k] - ptr->info;
			if ((len = snprintf(str,5, "%d->%d :\0", j, k)) < 0)
				cout << "sprintf error" << endl;

			Act.insert(make_pair(str, make_pair(ee, el)));
			if (ee == el){
				if ((mptr=mp.find(j)) == mp.end()) {//mp中尚没有指定关键字
					st.push_back(k);
					mp.insert(make_pair(j, st));
				}
				else {
					mptr->second.push_back(k);
				}
			}
		}
		st.clear();
	}
	cout << "关键活动:" << endl;
	for (auto c : mp){
		if(c.second.size()==1)
			cout << c.first << "->"<< *c.second.begin() << endl;
		else {
			for (auto s : c.second)
				cout << c.first << "->" << s << endl;
		}
	}
	//从开始节点(编号0)搜索最长关键路径,最后一个为完成节点(可能有多条关键路径)
	cout <<"最长关键路径:" << endl;
	show_critical(G->vexnum);
	return 0;
}

int main() {
	int r;

	ALGraph *graph = Establish_Graph();
	if((r=CriticalPath(graph))<0)
		cout<<"CriticalPath error"<<endl;
	free(graph);

	cout << "拓扑序列:" << endl;
	for (auto c : TopOrder) {
		cout << c << " ";
	}
	cout <<endl<< "各活动的最早与最迟开始时间:" << endl;
	for (auto c : Act) {
		cout << c.first << "( " << c.second.first << " , " << c.second.second << " )" << endl;
	}
	cout << "各节点的ve:" << endl;
	for (auto c : ve)
		cout << c << " ";
	cout << endl << "各节点的vl:" << endl;
	for (auto c : vl)
		cout << c << " ";
	cout << endl;

	system("pause");
	return 0;
}
运行结果:


经验证,上述方法可以推广,可以正确解决AOE问题。


  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AOE(Activity On Edge)也叫 PERT 图,是一种用于表示项目进度计划的网络图。在 AOE 中,每个事件作为一个节点,每个活动作为一条有向边。每个事件和活动都有一个时间长度。关键路径指的是连接起点和终点的路径,这条路径上的活动所需要的时间总和最长,因此决定了整个项目的最短完成时间。 要 AOE 关键路径,可以使用 C 语言实现关键路径算法。具体的实现步骤如下: 1. 定义一个结构体来表示 AOE 中的节点,包括节点编号、活动编号、活动持续时间、前继节点个数、后继节点个数、前继节点和后继节点数组等信息。 2. 构建 AOE ,读入每个节点的信息,建立节点之间的关系。 3. 计算每个节点的最早开始时间 EST 和最迟开始时间 LST,以及每个活动的最早开始时间 EFT 和最迟开始时间 LFT。通过这些时间可以确定关键路径上的活动。 4. 输出关键路径上的活动,并计算整个项目的最短完成时间。 下面是 C 语言实现关键路径算法的伪代码: ```c struct node { int id; // 节点编号 int act_id; // 活动编号 int act_time; // 活动持续时间 int pred_cnt; // 前继节点个数 int succ_cnt; // 后继节点个数 int *preds; // 前继节点数组 int *succs; // 后继节点数组 int est, lst; // 最早开始时间、最迟开始时间 int eft, lft; // 最早结束时间、最迟结束时间 }; int main() { // 构建 AOE struct node nodes[N]; int n = read_input(nodes); build_network(nodes, n); // 计算 EST 和 LST calc_est_lst(nodes, n); // 计算 EFT 和 LFT calc_eft_lft(nodes, n); // 输出关键路径上的活动 print_critical_activities(nodes, n); // 计算项目最短完成时间 int project_time = calc_project_time(nodes, n); printf("Project time: %d\n", project_time); return 0; } ``` 其中,`read_input()` 函数用于读入 AOE 中的节点信息,`build_network()` 函数用于构建节点之间的关系,`calc_est_lst()` 函数用于计算每个节点的最早开始时间 EST 和最迟开始时间 LST,`calc_eft_lft()` 函数用于计算每个活动的最早开始时间 EFT 和最迟开始时间 LFT,`print_critical_activities()` 函数用于输出关键路径上的活动,`calc_project_time()` 函数用于计算整个项目的最短完成时间。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值