2021-12-23【数据结构课程设计】【关键路径】

关键路径:

通常把计划、施工过程、生产流程、程序流程等都当成一个工程。工程通常分为若干个称为“活动”的子工程。完成了这些“活动”,这个工程就可以完成了。通常用 AOE-网来表示工程。AOE-网是一个带权的有向无环图,其中,顶点表示事件(EVENT),表示活动,权表示活动持续的时间。 AOE-网可以用来估算工程的完成时间。可以使人们了解:

(1)研究某个工程至少需要多少时间?
(2)哪些活动是影响工程进度的关键?

由于 AOE-网中的有些活动可以并行进行,从开始点到各个顶点,以致从开始点到完成点的有向路径可能不止一条,这些路径的长度也可能不同。完成不同路径的活动所需的时间虽然不同,但只有各条路径上所有活动都完成了,这个工程才算完成。因此,完成工程所需的最短时间是从开始点到完成点的最长路径的长度,即在这条路径上的所有活动的持续时间之和.这条路径长度就叫做关键路径(Critical Path)。

例:AOE 图如下:
在这里插入图片描述

程序执行结束后应该输出:

关键活动为: a1,a4,a7,a10,a8,a11

关 键 路 径 为 : a1->a4->a7->a10 ( 或 者 V1->V2->V5->V7->V9 ) 和a1->a4->a8->a11(或者 V1->V2->V5->V8->V9

花费的时间为至少为 :18(时间单位)。

算法设计:

1、 用邻接表存储一张带权有向图。
2、 对图进行拓扑排序,并进行事件的最早发生时间Ve[i]的计算。
3、 根据排序结果,判断图中是否存在有向环。
4、 根据逆拓扑序列,计算事件的最晚发生时间Vl[i]。
5、 计算活动的最早、最晚发生时间,判断关键活动,找出关键路径。

流程图:

在这里插入图片描述

算法实现:

#include<bits/stdc++.h>
using namespace std;

typedef int Status;
typedef int Boolean; 
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define MAX_NAME 5 
typedef int InfoType;
typedef char VertexType[MAX_NAME];
/* ---------------------------------  图的邻接表存储表示    --------------------------------*/
#define MAX_VERTEX_NUM 20
typedef struct ArcNode{         //表结点
	int adjvex;                 //该弧所指向的顶点的下标
	struct ArcNode *nextarc;    //指向下一条弧的指针 
	InfoType *info;             //边权值
}ArcNode;
typedef struct{
	VertexType data;            //顶点信息
	ArcNode *firstarc;          //第一个表结点的地址,指向第一条依附该顶点的弧的指针
}VNode, AdjList[MAX_VERTEX_NUM];//邻接表
typedef struct{                 //图
	AdjList vertices;
	int vexnum, arcnum;         //顶点数和边数
	int kind;                   //图的种类
}ALGraph;
/* -----------------------------   需要用的图的邻接表存储的基本操作  --------------------------*/ 
int LocateVex(ALGraph G, VertexType u){ 
  //若G中存在顶点u,则返回该顶点在图中位置;否则返回-1
	int i;
	for (i = 0; i < G.vexnum; ++i)
		if (strcmp(u, G.vertices[i].data) == 0)
			return i;
	return -1;
}

Status CreateGraph(ALGraph &G){ //创建有向网
	int i, j, k;
	int w; // 权值 
	VertexType va, vb;
	ArcNode *p;
    G.kind = 1;//有向网
    printf("请输入图的顶点数和边数(空格隔开): ");
	scanf("%d%d", &G.vexnum, &G.arcnum);
	printf("请输入%d个顶点的值(<%d个字符):\n", G.vexnum, MAX_NAME);
	for (i = 0; i < G.vexnum; ++i) {
		scanf("%s", G.vertices[i].data);
		G.vertices[i].firstarc = NULL;
	}
	printf("请顺序输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n");
	for (k = 0; k < G.arcnum; ++k) {
		scanf("%d%s%s", &w, va, vb);
		i = LocateVex(G, va); // 弧尾 
		j = LocateVex(G, vb); // 弧头 
		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex = j;
		p->info = (int *)malloc(sizeof(int));
		*(p->info) = w;
		p->nextarc = G.vertices[i].firstarc; // 插在表头 
		G.vertices[i].firstarc = p;
	}
	return OK;
}

void Display(ALGraph G){ // 输出图的邻接表G 
	int i;
	ArcNode *p;
	printf("有向图\n");
	printf("%d个顶点:\n", G.vexnum);
	for (i = 0; i < G.vexnum; ++i)
		printf("%s ", G.vertices[i].data);
	printf("\n%d条弧(边):\n", G.arcnum);
	for (i = 0; i < G.vexnum; i++){
		p = G.vertices[i].firstarc;
		while (p){
            printf("%s→%s :%d", G.vertices[i].data, G.vertices[p->adjvex].data, *(p->info));
			p = p->nextarc;
		}
		printf("\n");
	}
}
void FindInDegree(ALGraph G, int indegree[]){ // 求顶点的入度
	int i;
	ArcNode *p;
	for (i = 0; i < G.vexnum; i++)
		indegree[i] = 0; /* 赋初值 */
	for (i = 0; i < G.vexnum; i++){
		p = G.vertices[i].firstarc;
		while (p){
			indegree[p->adjvex]++;
			p = p->nextarc;
		}
	}
}
typedef int SElemType; 
/* -----------------------------------   栈的顺序存储表示   -----------------------------------*/
#define STACK_INIT_SIZE 10
#define STACKINCREMENT 2 
typedef struct SqStack{
	SElemType *base; 
	SElemType *top;
	int stacksize;
}SqStack;
Status InitStack(SqStack &S){ 
	S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if (!S.base)
		exit(_OVERFLOW);
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
	return OK;
}
 
Status StackEmpty(SqStack S){
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}
 
Status GetTop(SqStack s,SElemType &e){
    if(s.base==s.top)
        return 0;
    e = *(s.top - 1);
    return 1;
}

Status Push(SqStack &S, SElemType e){ 
	if (S.top - S.base >= S.stacksize) {
		S.base = (SElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));
		if (!S.base)
			exit(_OVERFLOW);
		S.top = S.base + S.stacksize;
		S.stacksize += STACKINCREMENT;
	}
	*(S.top)++ = e;
	return OK;
}
 
Status Pop(SqStack &S, SElemType &e){ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
	if (S.top == S.base)
		return ERROR;
	e = *--S.top;
	return OK;
}

int ve[MAX_VERTEX_NUM], vl[MAX_VERTEX_NUM];//e[]:最早发生时间 vl[]:最迟发生时间
bool flag = 0;
vector<string> path;
int top,down;
void dfs(ALGraph G, int u){
	if (G.vertices[u].data == G.vertices[top].data) {//搜索到终点
		if (flag) cout << "\n或者:\n";
		cout << G.vertices[down].data;	//输出起点
		for(auto &it:path)	//遍历输出关键路径
            cout << it;
        flag = 1;			
		return;
	}
	ArcNode* p = G.vertices[u].firstarc;	//初边
	for (; p; p = p->nextarc) {				//遍历
		int k = p->adjvex;					//该弧所指向的顶点的下标
		int dut = *(p->info);				//弧的权值
		if (ve[u] == vl[k] - dut) {			//是关键活动
            path.push_back("->");			
            path.push_back(G.vertices[k].data);		//记录关键活动
            dfs(G, k);				//继续DFS搜索下一个结点的关键活动
            path.pop_back();		//回溯
            path.pop_back();		//回溯
        }
	}
}

//算法7.13
Status TopologicalOrder(ALGraph G, SqStack &T){ 
	int j, k, count, indegree[MAX_VERTEX_NUM],e;
	SqStack S;
	ArcNode *p;
	FindInDegree(G, indegree);
	InitStack(S); 
	for (j = 0; j < G.vexnum; ++j) 
		if (!indegree[j])
			Push(S, j);
	GetTop(S, down);	//记录起点位置
	InitStack(T); 
	count = 0; 
	for (j = 0; j < G.vexnum; ++j) 
		ve[j] = 0;	//初始化
	while (!StackEmpty(S)){
		Pop(S, j);
		Push(T, j);
		++count;	//入栈的顶点数
		for (p = G.vertices[j].firstarc; p; p = p->nextarc){	//遍历邻接表
			k = p->adjvex;          //该弧所指向的顶点的下标
			if (--indegree[k] == 0) //入度减为零入栈
				Push(S, k);
			if (ve[j] + *(p->info) > ve[k])//拓扑正序更新max
				ve[k] = ve[j] + *(p->info);
		}
	}
	if (count < G.vexnum){
		printf("此有向网有回路\n");
		return ERROR;
	}
	else{
        GetTop(T, top);//拓扑排序的最后一个结点为终点
        return OK;
    }
		
}
//算法7.14
Status CriticalPath(ALGraph G){
	SqStack T;
	int i, j, k, ee, el;
	ArcNode *p;
	char dut, tag;
	if (!TopologicalOrder(G, T)) // 产生有向环 
		return ERROR;
	j = ve[0];
	for (i = 1; i < G.vexnum; i++) //j=Max(ve[]) 完成点的值 
		if (ve[i] > j)
			j = ve[i];
	int maxn = j;
	for (i = 0; i < G.vexnum; i++) // 初始化顶点事件的最迟发生时间(最大值) 
		vl[i] = j; // 完成点的最早发生时间 
	while (!StackEmpty(T)) // 按拓扑逆序求各顶点的vl值 
		for (Pop(T, j), p = G.vertices[j].firstarc; p; p = p->nextarc){
			k = p->adjvex;
			dut = *(p->info); // dut<j,k> 
			if (vl[k] - dut < vl[j])
				vl[j] = vl[k] - dut;
		}
	printf(" j  k  dut  ee  el  tag\n");
	for (j = 0; j < G.vexnum; ++j)//求ee,el和关键活动 
		for (p = G.vertices[j].firstarc; p; p = p->nextarc){
			k = p->adjvex;
			dut = *(p->info);//边权
			ee = ve[j];
			el = vl[k] - dut;
			tag = (ee == el) ? '*' : ' ';
			printf("%2d %2d %3d %3d %3d    %c\n", j, k, dut, ee, el, tag);
		}
	printf("关键活动为:\n");
	for (j = 0; j < G.vexnum; ++j) 
		for (p = G.vertices[j].firstarc; p; p = p->nextarc){
			k = p->adjvex;
			dut = *(p->info);
			if (ve[j] == vl[k] - dut)
				printf("%s→%s\n", G.vertices[j].data, G.vertices[k].data); // 输出关键活动 
		}
	printf("关键路径为:\n");
    dfs(G,0);
    printf("\n花费时间最少为:%d\n", maxn); 
	return OK;
}
 
int main(){
	ALGraph h;
	CreateGraph(h);
	Display(h);
	CriticalPath(h);
}

测试样例:

在这里插入图片描述
再加两条:
v1 -> v10(权值为8)
v10 -> v9(权值为10)

测试数据:
请输入图的顶点数和边数(空格隔开): 10 13
请输入10个顶点的值(<5个字符):
v1 v2 v3 v4 v5 v6 v7 v8 v9 v10
请顺序输入每条弧()的权值、弧尾和弧头(以空格作为间隔):
6 v1 v2
4 v1 v3
5 v1 v4
1 v2 v5
1 v3 v5
2 v4 v6
9 v5 v7
7 v5 v8
4 v6 v8
2 v7 v9
4 v8 v9
8 v1 v10
10 v10 v9
运行结果
有向图
10个顶点:
v1 v2 v3 v4 v5 v6 v7 v8 v9 v10
13条弧():
v1→v10 :8v1→v4 :5v1→v3 :4v1→v2 :6
v2→v5 :1
v3→v5 :1
v4→v6 :2
v5→v8 :7v5→v7 :9
v6→v8 :4
v7→v9 :2
v8→v9 :4

v10→v9 :10
 j  k  dut  ee  el  tag
 0  9   8   0   0    *
 0  3   5   0   3
 0  2   4   0   2
 0  1   6   0   0    *
 1  4   1   6   6    *
 2  4   1   4   6
 3  5   2   5   8
 4  7   7   7   7    *
 4  6   9   7   7    *
 5  7   4   7  10
 6  8   2  16  16    *
 7  8   4  14  14    *
 9  8  10   8   8    *
关键活动为:
v1→v10
v1→v2
v2→v5
v5→v8
v5→v7
v7→v9
v8→v9
v10→v9
关键路径为:
v1->v10->v9
或者:
v1->v2->v5->v8->v9
或者:
v1->v2->v5->v7->v9
花费时间最少为:18

心得体会

  1. 在使用邻接表存图过程中再次熟悉链表相关操作。
  2. 复习栈的编写及使用。利用栈求解拓扑序列。
  3. 在进行拓扑排序时,掌握结点度(入度)求取,学会拓扑有序的应用:利用拓扑排序进行最早发生时间的计算。
  4. 复习栈的编写及使用。利用栈存储拓扑有序序列,出栈即得到拓扑逆序序列。并利用拓扑逆序求解最晚发生时间的计算。
  5. 复习图上深度优先搜索。利用DFS输出关键路径。

参考文献

[1] 严蔚敏,吴伟民.数据结构(C语言版)[M]. 北京:清华大学出版社,1997.4

2021-12-30 更新

1. dfs()起点位置不正确:

错误代码:

dfs(G,0);

正确代码:

dfs(G,down);

0并不是起点的下标,down才是。
例如测试样例改为:

请输入图的顶点数和边数(空格隔开): 9 11
请输入9个顶点的值(<5个字符):
v2 v1 v3 v4 v5 v6 v7 v8 v9
请顺序输入每条弧()的权值、弧尾和弧头(以空格作为间隔):
6 v1 v2
4 v1 v3
5 v1 v4
1 v2 v5
1 v3 v5
2 v4 v6
9 v5 v7
7 v5 v8
4 v6 v8
2 v7 v9
4 v8 v9

dfs(G,0)会输出:

有向图
9个顶点:
v2 v1 v3 v4 v5 v6 v7 v8 v9 
11条弧():
v2→v5 :1
v1→v4 :5v1→v3 :4v1→v2 :6   
v3→v5 :1
v4→v6 :2
v5→v8 :7v5→v7 :9
v6→v8 :4
v7→v9 :2
v8→v9 :4

 j  k  dut  ee  el  tag    
 0  4   1   6   6    *     
 1  3   5   0   3          
 1  2   4   0   2          
 1  0   6   0   0    *     
 2  4   1   4   6          
 3  5   2   5   8          
 4  7   7   7   7    *     
 4  6   9   7   7    *     
 5  7   4   7  10     
 6  8   2  16  16    *
 7  8   4  14  14    *
关键活动为:
v2→v5
v1→v2
v5→v8
v5→v7
v7→v9
v8→v9
关键路径为:
v1->v5->v8->v9				//错误
或者:
v1->v5->v7->v9
花费时间最少为:18

dfs(G,down)会输出:

有向图
9个顶点:
v2 v1 v3 v4 v5 v6 v7 v8 v9 
11条弧():
v2→v5 :1
v1→v4 :5v1→v3 :4v1→v2 :6
v3→v5 :1
v4→v6 :2
v5→v8 :7v5→v7 :9
v6→v8 :4
v7→v9 :2
v8→v9 :4

 j  k  dut  ee  el  tag
 0  4   1   6   6    *
 1  3   5   0   3
 1  2   4   0   2
 1  0   6   0   0    *
 2  4   1   4   6
 3  5   2   5   8
 4  7   7   7   7    *
 4  6   9   7   7    *
 5  7   4   7  10
 6  8   2  16  16    *
 7  8   4  14  14    *
关键活动为:
v2→v5
v1→v2
v5→v8
v5→v7
v7→v9
v8→v9
关键路径为:
v1->v2->v5->v8->v9		//正确
或者:
v1->v2->v5->v7->v9
花费时间最少为:18

2. dfs()另一种记录方式:

char* strcmmp(char *a,int n){
	char *arr=(char*)malloc(n*sizeof(char));
	for(int i=0;i<n-1;i++){
		arr[i]=a[i];
	}
	arr[n-1]='\0';
	return arr;
}
void dfs(ALGraph G,int u,char arry[])
{
	if(G.adjlist[u].vex==G.adjlist[top].vex)
	{
		//printf("%s",G.adjlist[down].vex);
		for(int i=0;i<strlen(arry);i++)
		{
			printf("%c",arry[i]);
			if(i>=0&&i<strlen(arry)-1)
			printf("->");
		}
		printf("\n");
		return ;
	}
	EdgeNode *p=G.adjlist[u].firstedge;
	for(;p;p=p->next)
	{
		int k=p->adjvex,w=p->weight;
		if(etv[u]==ltv[k]-w)
		{	//printf("%d\n",k);
			arry=strcat(arry,G.adjlist[k].vex);
			dfs(G,k,arry);
			arry=strcmmp(arry,strlen(arry)-1);
		}
	
	}
}

3. C语言完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max_VERTEX_NUM 20
#define ElemType int
#define Status int
#define MAXVEX 100
#define MAX_NAME 5
int top, down;

int *etv, *ltv; //记录事件最早发生时间和最迟发生时间数组
typedef char VertexType[MAX_NAME];
typedef struct nodee //边表节点
{
	int adjvex;			//邻接点域,存储该顶点对应的下标
	int weight;			//权值
	struct nodee *next; //指向下一个顶点
} EdgeNode;

typedef struct vnode //顶点
{
	VertexType vex;		 //顶点信息
	EdgeNode *firstedge; //指向顶点的第一个邻接顶点
} VertexNode, AdjList[Max_VERTEX_NUM];

typedef struct AlGraph
{
	AdjList adjlist;
	int vexnum;	 //节点数目
	int edgenum; //边的数目
} ALGraph;
//定义一个栈
typedef struct Node
{
	ElemType data;	   //存储数据
	struct Node *next; //指向下一个元素
} node, *LinkStackptr;
typedef struct LS
{
	LinkStackptr top;
	int count; //记录栈中元素的个数
} LinkStack;

Status InitStack(LinkStack *s) //创建一个栈
{
	s->top = (LinkStackptr)malloc(sizeof(node));
	if (s->top == NULL)
		exit(0);
	s->top->next = NULL;
	s->count = 0;
	return 1; //建栈成功返回1
}

Status StackEmpty(LinkStack s) //判断是否为空
{
	if (s.count == 0)
		return 1; //若栈为空返回1
	return 0;	  //若栈不为空返回 0
}

Status GetTop(LinkStack s, int *e) //返回栈顶元素
{
	if (s.top == NULL)
		return 0;
	*e = s.top->data;
	return *e;
}

Status Push(LinkStack *s, ElemType e) //元素入栈(头插)
{
	LinkStackptr p = (LinkStackptr)malloc(sizeof(node));
	if (p == NULL)
		return 0;
	p->data = e;
	p->next = s->top;
	s->top = p; //指向第一个元素的指针
	s->count++; //栈中元素个数增加
	return 1;
}

Status pop(LinkStack *s, ElemType *e) //返回栈顶元素 ,并删除 (头删)
{
	if (s->top == NULL)
		return 0;
	LinkStackptr p = s->top->next; //指向下一个元素
	*e = s->top->data;			   //将首元素的赋给e
	free(s->top);
	s->top = p;				 //删除节点
	s->count = s->count - 1; //栈中元素个数减少
	return *e;				 //返回e
}

void CreateALG(ALGraph *ALG, int **indegree) //创建一个图(邻接表存储)
{
	printf("请输入节点的数目和边的数目 ");
	scanf("%d%d", &ALG->vexnum, &ALG->edgenum);
	int i, j, k, w;
	EdgeNode *e;
	for (i = 0; i < ALG->vexnum; i++)
	{
		getchar();
		//	printf("请输入第%d个节点的信息",i+1);
		scanf("%s", ALG->adjlist[i].vex); //输入结点信息
		ALG->adjlist[i].firstedge = NULL;
	}
	for (k = 0; k < ALG->edgenum; k++) //边表的建立
	{
		getchar();
		// printf("请输入第%d条边<vi,vj>上的顶点序号和权值",k+1);
		scanf("%d%d%d", &w, &i, &j);
		e = (EdgeNode *)malloc(sizeof(EdgeNode));
		e->adjvex = j - 1;
		e->weight = w;
		e->next = ALG->adjlist[i - 1].firstedge;
		ALG->adjlist[i - 1].firstedge = e; //将结点插入
	}
	*indegree = (int *)malloc(ALG->vexnum * sizeof(int)); //创建indegree数组求入度
	for (i = 0; i < ALG->vexnum; i++)
	{
		(*indegree)[i] = 0; //(indegree初始化)
	}
}

void FindInDegree(ALGraph ALG, int indegree[]) //求各个结点的入度
{
	int i;
	EdgeNode *temp;
	for (i = 0; i < ALG.vexnum; i++) //对每个顶点的边表都进行遍历
	{
		temp = ALG.adjlist[i].firstedge;
		while (temp != NULL)
		{
			indegree[temp->adjvex]++;
			temp = temp->next;
		}
	}
}
char *strcmmp(char *a, int n)
{
	char *arr = (char *)malloc(n * sizeof(char));
	for (int i = 0; i < n - 1; i++)
	{
		arr[i] = a[i];
	}
	arr[n - 1] = '\0';
	return arr;
}
void dfs(ALGraph G, int u, char arry[])
{
	if (G.adjlist[u].vex == G.adjlist[top].vex)
	{
		printf("关键路径为:\n");
		for (int i = 0; i < strlen(arry); i++)
		{
			printf("%c", arry[i]);
		}
		printf("\n");
		return;
	}
	EdgeNode *p = G.adjlist[u].firstedge;
	for (; p; p = p->next)
	{
		int k = p->adjvex, w = p->weight;
		if (etv[u] == ltv[k] - w)
		{
			arry = strcat(arry, "->");
			arry = strcat(arry, G.adjlist[k].vex);
			dfs(G, k, arry);
			arry = strcmmp(arry, strlen(arry) - 2);
			arry = strcmmp(arry, strlen(arry) - 1);
		}
	}
}
Status TopologicalSort(ALGraph ALG, int indegree[], LinkStack *T) //拓扑排序
{
	int i, count = 0, k;
	LinkStack s;
	EdgeNode *p;
	InitStack(T);  //创建一个栈 (用于逆序)
	InitStack(&s); //创建一个栈(用于正序)
	etv = (int *)malloc(ALG.vexnum * sizeof(int));
	for (i = 0; i < ALG.vexnum; i++)
	{
		etv[i] = 0; // 对记录事件最早发生时间的数组进行初始化
	}

	for (i = 0; i < ALG.vexnum; i++)
	{
		if (indegree[i] == 0)
		{
			Push(&s, i); //遍历找到入度为0的元素入栈
			GetTop(s, &down);
		}
	}
	while (!StackEmpty(s)) //循环
	{
		pop(&s, &i);					   //出栈
		Push(T, i);						   //入栈
		printf("%s ", ALG.adjlist[i].vex); //打印出拓扑正序序列
		count++;						   //记录输出的结点数
		for (p = ALG.adjlist[i].firstedge; p; p = p->next)
		{
			k = p->adjvex;
			if (!(--indegree[k]))			 //出栈的同时对各个结点的入度进行更新
				Push(&s, k);				 //进行判断,若入度为0便入栈
			if (etv[i] + p->weight > etv[k]) // 求事件的最早发生时间    //前->后
				etv[k] = etv[i] + p->weight;
		}
	}
	if (count < ALG.vexnum) //判断是否有回路
		return 0;			//若有回路返回0
	else
	{
		GetTop(*T, &top);
		return 1;
	} //若无回路返回1
}

Status CriticalPath(ALGraph ALG, LinkStack *T, int n) //求关键路径
{
	if (n == 0) //判断是否有回路(若有回路程序停止执行)
	{
		printf("有回路");
		return 0;
	}
	int i, k, w, j;
	int ete, lte; //活动的最早,最迟开始时间
	EdgeNode *p;
	ltv = (int *)malloc(ALG.vexnum * sizeof(int)); //对求事件最迟开始时间的数组进行初始化
	j = etv[0];
	for (i = 1; i < ALG.vexnum; i++)
	{
		if (j < etv[i])
			j = etv[i];
	}
	for (i = 0; i < ALG.vexnum; i++)
	{
		ltv[i] = j; //最后一个事件的最迟开始时间等于其最早开始时间
	}
	while (!StackEmpty(*T)) //利用拓扑逆序求事件的最迟开始时间
	{
		pop(T, &i);
		for (p = ALG.adjlist[i].firstedge; p; p = p->next)
		{
			k = p->adjvex;
			w = p->weight;
			if (ltv[k] - w < ltv[i]) //后->前
				ltv[i] = ltv[k] - w;
		}
	}
	for (i = 0; i < ALG.vexnum; i++)
	{
		for (p = ALG.adjlist[i].firstedge; p; p = p->next)
		{
			k = p->adjvex;
			w = p->weight;
			ete = etv[i];
			lte = ltv[k] - w;																		//求活动的最迟开始和最早开始时间
			if (ete == lte)																			//若相等便是关建活动
				printf("<%s,%s>length is %d\n", ALG.adjlist[i].vex, ALG.adjlist[k].vex, p->weight); //输出关键路径
		}
	}
	printf("\n");
	printf("路径长度为: %d", j);
	printf("\n");
	dfs(ALG, 0, ALG.adjlist[0].vex);
}
int main()
{
	ALGraph ALG;
	LinkStack T;
	int *indegree;
	int n;
	CreateALG(&ALG, &indegree);
	FindInDegree(ALG, indegree);
	printf("拓扑正序为:");
	n = TopologicalSort(ALG, indegree, &T);
	printf("\n");
	printf("关键路径为:\n");
	CriticalPath(ALG, &T, n);
}

【输入】

请输入节点的数目和边的数目 6 8
v1 v2 v3 v4 v5 v6
2 1 3
3 1 2
2 2 4
3 2 5
1 5 6
2 4 6
4 3 4
3 3 6

【输出】

拓扑正序为:v1 v3 v2 v4 v5 v6 
关键路径为:
<v1,v3>length is 2
<v3,v4>length is 4
<v4,v6>length is 2

路径长度为: 8
关键路径为:
v1->v3->v4->v6
  • 5
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eternity_GQM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值