Bootstrap

如何求解关键路径——怎样计算工程所需的最短时间

AOE-网如下:

把图中的AOE-网看作一个工程的话,则就需要解决下面两个问题:

  • 估算完成整项工程至少需要多少时间
  • 判断哪些活动是影响工程进度的关键

分析

     整个工程只有一个开始点和一个完成点,所以在正常的情况(无环)下,网中只有一个入度为零的点,称为源点,也只有一个出度为零的点,称为汇点。在整个AOE-网中,一条路径各弧上的权值之和称为该路径的带权路径长度。要估算整项工程完成的最短时间,就是要找一条从源点到汇点的带权路径最长的路径,称为关键路径。关键路径上的活动叫做关键活动,这些活动是影响工程进度的关键,他们提前或拖延将使整个工程提前或拖延。

确定关键路径

一,定义4个描述量

1,事件N的最早发生时间ve[N的下标]

进入事件N的每一活动都结束,N才能发生,所以ve[N的下标]是从源点到N的最长路径长度

2,事件N的最迟发生时间vl[N的下标]

事件N的发生不得延迟于N的每一后继事件的最迟发生时间。

为了不拖延工期,N的最迟发生时间不得迟于其后继事件M的最迟发生事件减去N到M的持续时间

eg:若N是B,M是E;则N的最迟发生时间不得迟于M的最迟发生时间减去MN之间的时间1.

3,活动的最早开始时间e[]

只有事件N发生了,活动x才能发生,所以,活动x的最早开始时间等于事件N的最早发生事件ve[N的下标]

eg:若N是B,x是代表BE这条弧,则只有事件B发生了,活动BE才能发生

4,活动的最晚开始时间l[]

活动x的开始事件需保证不延误事件N的最迟发生时间。所以,活动x的最晚开始时间等于事件N的最迟发生时间vl[N的下标]减去活动x持续的时间

eg:若N是E,x是代表BE这条弧,则BE不能厌恶N的发生,所以最晚开始时间等于vl[N]-BE;

二,关键路径求解过程

  1. 对图中顶点进行排序,在排序过程中按照拓扑序列求出每个事件的最早发生时间ve[];
  2. 按照逆拓扑排序求出每个事件的最迟发生时间vl[];
  3. 求出每个活动XY的最早开始时间e[];
  4. 求出每个活动XV的最晚开始时间l[];
  5. 如果e[i]=l[i],即为关键活动。由关键活动形成的由源点到汇点的每一条路径就是关键路径,关键路径可能不止一条

三,关键路径部分求解代码

int ve[pointMax];      //最早发生时间
int vl[pointMax];      //最晚发生时间

void CritPath(ATgroup *A)
{
	int n = A->point;          //n为顶点个数
	for (int i = 0; i < n; i++)        //将每个事件的最早事件为0
		ve[i] = 0;

	//---按拓扑次序求每个事件的最早发生时间---//
	int k, j;
	for (int i = 0; i < n; i++)
	{
		k = topo[i];                 //取的拓扑排序中的顶点序号
		cout <<"K=F"<< k << "  "<<endl;
		VtNode *p = A->vertices[k].Out;     //指向K的第一个邻接结点
		while (p != NULL)
		{
			j = p->peaceE;
			if (ve[j] < ve[k] + p->len)
			{
				ve[j] = ve[k] + p->len;
				cout << ve[j] << "  " << ve[k] << "   " << p->len << endl;
			}
			p = p->nextVtt;
		}
		cout << ve[j] << endl;
	}
	for (int i = 0; i < A->point; i++)
	{
		cout << ve[i] << "   ";
	}
	cout << endl;
	cout << endl;

	for (int i = 0; i < n; i++)       //初始化
	{
		vl[i] = ve[topo[n - 1]];
	}
	//---按逆拓扑排序求每个事件的最迟发生时间----//
	for (int i = n - 1; i >= 0; i--)
	{
		k = topo[i];                 //取的拓扑排序中的顶点序号
		VtNode *p = A->vertices[k].Out;     //指向K的第一个邻接结点
		//cout << k << endl;
		while (p != NULL)
		{
			j = p->peaceE;
			if (vl[k] > vl[j] - p->len)
			{
				vl[k] = vl[j] - p->len;
				//cout << vl[k] << "  " << vl[j] << "   " << p->len << endl;
			}
			p = p->nextVtt;
		}
		//cout << vl[j] << endl;
	}

	for (int i = 0; i < A->point; i++)
	{
		cout << vl[i] << "   ";
	}
	cout << endl;
	cout << endl;

	//----判断每一活动是否为关键活动-----//
	int e, l;
	for (int i = 0; i < n; i++)
	{
		VtNode *p = A->vertices[i].Out;
		while (p != NULL)
		{
			j = p->peaceE;
			e = ve[i];
			l = vl[j] - p->len;
			if (e == l)
			{
				cout << A->vertices[i].data << "   " << A->vertices[j].data << endl;
			}
			p = p->nextVtt;
		}
	}
}

四,整段程序代码

#include<iostream>
using namespace std;

#define pointMax 100

struct VtNode                 //权值信息
{
	VtNode *nextVt;           //入度链表下一个结点
	int peace;                //入度下一顶点的值

	VtNode *nextVtt;          //出度链表下一个结点
	int peaceE;               //出度下一顶点的值

	int len;
};
struct PoNode                  //顶点信息
{
	char data;
	VtNode *firstPo;          //入度
	VtNode *Out;              //出度
};

struct ATgroup
{
	PoNode vertices[pointMax];     //每一个verticse代表一个顶点
	int point, vert;               //point顶点数,vert弧数
};

struct Node
{
	int data;
	Node *next;
};

struct SqStack          //栈
{
	Node *base;          //栈底
	Node *top;           //栈顶
	int data;
};

void Push(SqStack &S, int i)       //入栈
{
	Node *m = new Node;
	m->data = i;
	m->next = S.top;             //入栈过程
	S.top = m;
}

int Pop(SqStack &S)              //出栈
{
	int n = S.top->data;
	S.top = S.top->next;         //出栈过程
	return n;
}


int ATlocate(ATgroup A, char x)             //找到位置
{
	for (int i = 0; i < A.point; i++)       //依次遍历点的信息
	{
		if (A.vertices[i].data == x)        //找到x的位置
		{
			return i;
		}
	}
}

void show(ATgroup &A)                      //显示当前所有点入度出度的顶点
{
	//cout << endl;
	for (int i = 0; i < A.point; i++)
	{
		//cout << i;
		if (A.vertices[i].firstPo != NULL)          //入度位置
		{
		//	cout << "    " << A.vertices[i].firstPo->peace << "   ";
		}
		//else
		//	cout << "    -1" << "    ";

		if (A.vertices[i].Out != NULL)             //出度位置
		{
		//	cout << A.vertices[i].Out->peaceE << endl;
		}
		//else
		//	cout << "    -1" << endl;
	}
}

void CreatAT(ATgroup &A)
{
	cout << "输入邻接矩阵顶点数:";
	cin >> A.point;
	cout << "输入邻接矩阵边数:";
	cin >> A.vert;
	getchar();
	char q[100];
	cout << "输入顶点信息:";
	gets_s(q);
	for (int i = 0; i < A.point; i++)
	{
		A.vertices[i].data = q[i];               //输入顶点值
		A.vertices[i].firstPo = NULL;            //初始化头结点为空
		A.vertices[i].Out = NULL;
	}
	char v1, v2; int m, n; int len;
	for (int i = 0; i < A.vert; i++)            //输入各边,构造邻接表
	{
		cout << "输入第" << i << "条边的依附的两个顶点:";
		int Q;
		cin >> v1 >> v2 >> Q;
		m = ATlocate(A, v1);                  //确定位置
		n = ATlocate(A, v2);
		//第一个
		VtNode *p1 = new VtNode;
		VtNode *p2 = new VtNode;
		p1->peace = m;                             //入度
		p1->nextVt = A.vertices[n].firstPo;
		A.vertices[n].firstPo = p1;

		p2->peaceE = n;                            //出度
		p2->nextVtt = A.vertices[m].Out;
		p2->len = Q;
		A.vertices[m].Out = p2;
	}
	//show(A);
}

void FindIn(ATgroup *A, int *in)           //统计所有点的入度数并存入到in数组中
{
	int n = 0;
	for (int i = 0; i < A->point; i++)     //遍历每一个点
	{
		VtNode *p = new VtNode;
		p = A->vertices[i].firstPo;
		while (p != NULL)                  //将入度链表进行遍历
		{
			n++;
			p = p->nextVt;          //下一结点
		}
		in[i] = n;          //存入in数组
		n = 0;
	}
}

void SHOW(int *a, ATgroup *A)           //显示当前所有顶点入度数量还有几个
{
	for (int i = 0; i < A->point; i++)
	{
		//cout << a[i] << "  ";
	}
	//cout << endl;
}

int M[pointMax] = { 0 };
int topo[pointMax];           //拓扑遍历存入

void TPsort(ATgroup *A, SqStack &S)           //拓扑排序过程
{
	int Indegree[pointMax];
	FindIn(A, Indegree);             //将入度赋值给数组

	for (int i = 0; i < A->point; i++)
	{
		if (Indegree[i] == 0)         //将所有入度等于0的入栈
		{
			//cout << "Push=" << i << endl;
			Push(S, i);
		}
	}

	int m = 0;                //统计入度的顶点数
	int n, k;

	while (S.base != S.top)       //判断是否遍历完
	{
		//cout << endl;
		n = Pop(S);         //栈顶出栈
		//cout << "Pop=" << n << endl;
		topo[m] = n;        //存入topo
		m++;
		VtNode* p = new VtNode;
		p = A->vertices[n].Out;             //出度链表的结点
		while (p != NULL)            //遍历出度链表
		{
			k = p->peaceE;          //某结点的位置
			//cout << "出度下一结点k=" << k << endl;
			Indegree[k]--;          //将该结点顶点位置入度减一
			//SHOW(Indegree, A);       //显示当前所有点的入度
			if (Indegree[k] == 0)      //当等于0时,入栈
			{
				//cout << "Push=" << k << endl;
				Push(S, k);
			}
			p = p->nextVtt;     //下一个
		}
	}
}



int ve[pointMax];      //最早发生时间
int vl[pointMax];      //最晚发生时间

void CritPath(ATgroup *A)
{
	int n = A->point;          //n为顶点个数
	for (int i = 0; i < n; i++)        //将每个事件的最早事件为0
		ve[i] = 0;

	//---按拓扑次序求每个事件的最早发生时间---//
	int k, j;
	for (int i = 0; i < n; i++)
	{
		k = topo[i];                 //取的拓扑排序中的顶点序号
		cout <<"K=F"<< k << "  "<<endl;
		VtNode *p = A->vertices[k].Out;     //指向K的第一个邻接结点
		while (p != NULL)
		{
			j = p->peaceE;
			if (ve[j] < ve[k] + p->len)
			{
				ve[j] = ve[k] + p->len;
				cout << ve[j] << "  " << ve[k] << "   " << p->len << endl;
			}
			p = p->nextVtt;
		}
		cout << ve[j] << endl;
	}
	for (int i = 0; i < A->point; i++)
	{
		cout << ve[i] << "   ";
	}
	cout << endl;
	cout << endl;

	for (int i = 0; i < n; i++)       //初始化
	{
		vl[i] = ve[topo[n - 1]];
	}
	//---按逆拓扑排序求每个事件的最迟发生时间----//
	for (int i = n - 1; i >= 0; i--)
	{
		k = topo[i];                 //取的拓扑排序中的顶点序号
		VtNode *p = A->vertices[k].Out;     //指向K的第一个邻接结点
		//cout << k << endl;
		while (p != NULL)
		{
			j = p->peaceE;
			if (vl[k] > vl[j] - p->len)
			{
				vl[k] = vl[j] - p->len;
				//cout << vl[k] << "  " << vl[j] << "   " << p->len << endl;
			}
			p = p->nextVtt;
		}
		//cout << vl[j] << endl;
	}

	for (int i = 0; i < A->point; i++)
	{
		cout << vl[i] << "   ";
	}
	cout << endl;
	cout << endl;

	//----判断每一活动是否为关键活动-----//
	int e, l;
	for (int i = 0; i < n; i++)
	{
		VtNode *p = A->vertices[i].Out;
		while (p != NULL)
		{
			j = p->peaceE;
			e = ve[i];
			l = vl[j] - p->len;
			if (e == l)
			{
				cout << A->vertices[i].data << "   " << A->vertices[j].data << endl;
			}
			p = p->nextVtt;
		}
	}
}

int main()
{
	ATgroup *A = new ATgroup;
	SqStack *S = new SqStack;
	S->top = S->base;
	S->data = pointMax;
	CreatAT(*A);
	TPsort(A, *S);
	CritPath(A);
	system("pause");
}

;