图的应用4.0-----关键路径(AOE网)

news2024/10/4 1:28:35

目录

前言

AOE网

1.基本概念

2.应用

关键路径

1.算法理论

2.代码实现(C/C++)


 

前言

        前面学习了图AOV网的应用,即拓扑排序,那这一期我们学习AOE网的应用,这是一个图的一个很广泛问题,也就是关键路径。那什么是关键路径呢?下面就一起来看看。

AOE网

1.基本概念

        把工程计划表示为边表示活动的网络,即AOE网用顶点表示事件,弧表示活动,弧的权表示活动持续时间。

        事件表示在它之前的活动已经完成,在它之后的活动可以开始

 如图所示,这就是一个AOE网的问题

任意应该AOE网都有一个起点和终点,那这里就称作为源点和汇点。 

源点与汇点

源点:入度为0的点,表示一个工程的开始。

汇点:出度为0的点,表示一个工程的结束。

2.应用

对于AOE网,我们关心两个问题

  1. 完成整项工程至少需要多少时间?
  2. 哪些活动是影响工程进度的关键?

AOE网可以很好的表示一项工程或者项目经历的流程和时间,那现在提出一个问题:如何从起点到终点经历过的总时间最长呢?(完成这个工程需要的最长时间)

这里就要用到关键路径的算法了,下面我们就来看看关键路径是怎么q求的。

关键路径

关键路路径-----路径长度最长的路径
路径长度------路径上各活动持续时间之和。

1.算法理论

 以这个图为示例,下面要弄懂4个数组数据的值

1.  ve(vj)------表示事件 vj的最早发生时间

         例: ve(v1) = 0 ve(v2) = 30

2.vl(vj)-----表示事件 vj 的最迟发生时间

         例: vl(v4) = 165

3.e(i)-----表示活动ai的最早开始时间

        例:e(a3) = 30

4.l(i)-----表示活动ai 的最迟开始时间

         例: l(a3) = 120

l(i) - e(i)-表示完成活动 ai 的时间余量。 例: (3) - e(3) = 90

关键活动

关键活动关键路径上的活动,即 l(i) == e(i) (即 l(i) - e(i) ==0 ) 的活动

以上4个量的关系: 

1.如何找l)== e(i)的关键活动?

设活动 ai 用弧 <j,k> 表示,其持续时间记为: Wj,k

则有: (1) e(i) = ve(j)

         (2) l(i) = vl(k) - Wj, k

2.如何求 ve(j)和 vl(j) ?

(1) 从 ve(1)=0开始向前递推

        ve(j) = Max{ve(i) + Wij}, < i,j >属于T, 2 <=j <=n

        其中T是所有以i为头的弧的集合。

(2)从 vl(n)= ve(n)开始向后递推

        vl(i) = Min{vl(j) - Wij}, < i, j >属于S, 1<=i<=n -1

        其中 S 是所有以i为尾的弧的集合。

注意:最短路径与最小生成树不同,路径上不一定包含 n个顶点,也不定包含 n-1条边 

求解过程,以下图为案例求解ve、vl、e、和l数组。

 

求关键路径步骤:

  1. 求 ve(i)、vl(j)
  2. 求e(i)、l(i)
  3. 计算 l(i) - e(i),如果值为0那就是关键路径中的一条路径
  4. 套用以下公式即可

求得的ve和vl如图所示:

求得的e和l,如图所示:

2.代码实现(C/C++)

邻接矩阵图的结构体:

#define Maxint 32767
#define Maxnum 100//最大顶点数

//数据类型
typedef struct datatype {
	char id[10];
	//……
}
ElemType;
//图的邻接数组
typedef struct graph {
	ElemType vexs[Maxnum];//图数据
	int matrix[Maxnum][Maxnum];//二维数组矩阵
	int vexnum;//点数
	int arcnum;//边数
}Graph;

关键路径算法代码: 

//获取入度为0的起始点下标
int in_degree0(Graph G) {
	int* sum_degree = (int*)malloc(sizeof(int) * G.vexnum);
	memset(sum_degree, 0, sizeof(int) * G.vexnum);
	for (int x = 0; x < G.vexnum; x++) {
		for (int y = 0; y < G.vexnum; y++) {
			if (G.matrix[y][x] != 0 && G.matrix[y][x] != Maxint) {
				sum_degree[x]++;
			}
		}
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (sum_degree[i] == 0) {
			free(sum_degree);
			sum_degree = NULL;
			return i;
		}
	}

}
//获取出度为0的交汇点下标
int out_degree0(Graph G) {
	int* sum_degree = (int*)malloc(sizeof(int) * G.vexnum);
	memset(sum_degree, 0, sizeof(int) * G.vexnum);
	for (int x = 0; x < G.vexnum; x++) {
		for (int y = 0; y < G.vexnum; y++) {
			if (G.matrix[x][y] != 0 && G.matrix[x][y] != Maxint) {
				sum_degree[x]++;
			}
		}
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (sum_degree[i] == 0) {
			free(sum_degree);
			sum_degree = NULL;
			return i;
		}
	}
}

//队列的结构体
typedef struct q {
	int prev;//上一个顶点下标
	int next;//下一个顶点下标
}Queue;

//边的结构体  起点 长度 终点
typedef struct e {
	int begin_index;//这个边的起始点
	int e;//边长度
	int end_index;//这个边的终点
}E;

//关键路径算法AOE
void Critical_path(Graph G) {
	//申请ve,vl,e,l的空间
	int* ve = (int*)malloc(sizeof(int) * G.vexnum);
	int* vl = (int*)malloc(sizeof(int) * G.vexnum);
	E* e = (E*)malloc(sizeof(E) * G.arcnum);
	int* l = (int*)malloc(sizeof(int) * G.arcnum);
	Queue* queue = (Queue*)malloc(sizeof(Queue) * G.vexnum);

	//01--处理ve
	for (int k = 0; k < G.vexnum; k++) {
		queue[k].prev = queue[k].next = -1;
		ve[k] = 0;
	}
	//对第一个数据处理
	int begin = in_degree0(G);
	//初始化
	int qu_count = 0;
	queue[qu_count].prev = queue[qu_count].next = begin;
	qu_count++;
	//后继处理
	while (qu_count) {
		//出队操作
		Queue pop = queue[0];
		//后继数据前移
		for (int d = 0; d < qu_count - 1; d++) {
			queue[d] = queue[d + 1];
		}
		qu_count--;
		//获取到出队顶点最早开始时间ve
		int max = ve[pop.prev] + G.matrix[pop.prev][pop.next];
		if (ve[pop.next] <= max)
			ve[pop.next] = max;

		//把当前出队的顶点后继连接的顶点入队
		for (int j = 0; j < G.vexnum; j++) {
			if (G.matrix[pop.next][j] != 0 && G.matrix[pop.next][j] != Maxint) {
				queue[qu_count].next = j;
				queue[qu_count].prev = pop.next;
				qu_count++;
			}
		}
	}

	//02--处理vl
	int end = out_degree0(G);
	for (int k = 0; k < G.vexnum; k++) {
		queue[k].prev = queue[k].next = -1;
		vl[k] = ve[end];
	}
	//对第一个数据处理
	queue[qu_count].next = queue[qu_count].prev = end;
	qu_count++;
	while (qu_count) {
		//出队
		Queue pop = queue[0];
		//后继数据前移
		for (int d = 0; d < qu_count - 1; d++) {
			queue[d] = queue[d + 1];
		}
		qu_count--;
		//比较找到最小值赋予到vl
		int min = vl[pop.next] - G.matrix[pop.prev][pop.next];
		if (min <= vl[pop.prev])
			vl[pop.prev] = min;
		//把当前出队的顶点前面相连的顶点入队
		for (int i = 0; i < G.vexnum; i++) {
			if (G.matrix[i][pop.prev] != 0 && G.matrix[i][pop.prev] != Maxint) {
				queue[qu_count].prev = i;
				queue[qu_count].next = pop.prev;
				qu_count++;
			}
		}
	}


	//03--处理e
	int e_count = 0;
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			if (G.matrix[i][j] != 0 && G.matrix[i][j] != Maxint) {
				e[e_count].begin_index = i;
				e[e_count].e = ve[i];//套用公式
				e[e_count].end_index = j;
				e_count++;
			}
		}
	}

	//04--处理l
	int l_count = 0;
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			if (G.matrix[i][j] != 0 && G.matrix[i][j] != Maxint) {
				//套用公式
				l[l_count++] = vl[j] - G.matrix[i][j];
			}
		}
	}

	//输出结果
	printf("\nve数组结果:");
	for (int j = 0; j < G.vexnum; j++) {
		printf("%d ", ve[j]);
	}
	printf("\nvl数组结果:");
	for (int j = 0; j < G.vexnum; j++) {
		printf("%d ", vl[j]);
	}
	printf("\ne的结果:");
	for (int j = 0; j < G.arcnum; j++) {
		printf("%d ", e[j].e);
	}
	printf("\nl的结果:");
	for (int j = 0; j < G.arcnum; j++) {
		printf("%d ", l[j]);
	}
	printf("\n关键路径路线如下:\n");
	for (int j = 0; j < G.arcnum; j++) {
		if (e[j].e - l[j] == 0) {
			int start = e[j].begin_index;
			int end = e[j].end_index;
			printf("%s->%s %d\n", G.vexs[start].id, G.vexs[end].id, G.matrix[start][end]);
		}
	}
	
	//释放空间
	free(ve);
	free(vl);
	free(e);
	free(l);
	free(queue);
	ve = vl = e = l = queue = NULL;
}

测试结果:

以上就是本期的内容了,喜欢的话点个赞吧!

 分享一张壁纸:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1140806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

红色荧光PLGA聚乙酸-羟基乙酸共聚物纤维膜 红色荧光PLGA静电纺丝纤维膜

一种含有荧光染料的硝酸纤维素微孔膜及其制备方法。将提供供体荧光分子的荧光染料直接加到铸膜液中&#xff0c;经相转换直接生产出一种带荧光的硝酸纤维素微孔膜&#xff0c;微孔膜中的荧光强度可以通过添加不同剂量的荧光素来控制调节&#xff0c;该微孔膜适用于生产荧光淬灭…

京东平台数据分析:2023年9月京东扫地机器人行业品牌销售排行榜

鲸参谋监测的京东平台9月份扫地机器人市场销售数据已出炉&#xff01; 根据鲸参谋平台的数据显示&#xff0c;9月份&#xff0c;京东平台扫地机器人的销量近14万&#xff0c;环比增长约2%&#xff0c;同比降低约4%&#xff1b;销售额为2.9亿&#xff0c;环比降低约4%&#xff0…

行情分析——加密货币市场大盘走势(10.27)

目前大饼开始了震荡盘整&#xff0c;目前远离EMA21均线&#xff0c;预计会有大的回调动作。而MACD日线来看&#xff0c;昨日和今日开始呈现绿色空心柱&#xff0c;也在说明大饼在做震荡盘整。不排除大跌的可能性&#xff0c;大饼可以开始布局中长线空单&#xff0c;可以再35000…

企业im即时通讯软件私有化部署,确保信息安全与高效办公

随着企业应用即时通讯&#xff08;IM&#xff09;的普及&#xff0c;信息安全问题成为了企业最为关心的话题。为了保障内部信息安全&#xff0c;让员工专心办公&#xff0c;企业可以选择将IM系统私有化部署在内网&#xff0c;与互联网隔离开来。 对于需要与外网通信的需求&…

Vue引入异步组件

defineAsyncComponent 函数&#xff1a;异步引入组件。 Suspense 标签&#xff1a;异步引入组件时&#xff0c;显示默认的内容。 异步引入组件的基本使用&#xff1a; 异步引入组件&#xff1a; import { defineAsyncComponent } from vue; const Child defineAsyncComponen…

基于安卓android微信小程序的投票系统

项目介绍 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;投票系统小程序被用户普遍使用&#xff0c;为方便用户…

关于JAVA中字节码文件版本号、产品版本号及开发版本号的关系

目录 关于字节码版本对应关系清单关于字节码格式说明的资料关于这些版本号 关于字节码版本 以二进制打开字节码文件&#xff1a; 如上图中第5-8标识&#xff08;圈起来的&#xff09;的即字节码版本号 十六进制&#xff1a; 34 十进制&#xff1a; 52 jdk 8 对应关系清单 …

网络策略实战

网络策略实战 网络策略 在命名空间 dev 中创建⽹络策略 dev-policy&#xff0c;只允许 命名空间 prod 中的 pod 连上 dev 中 pod 的 80 端⼝&#xff0c;注意:这⾥有 2 个 ns &#xff0c;⼀个为 dev(⽬标pod的ns)&#xff0c;另外⼀个为prod(访 问源pod的ns) &#x1f50b;创建…

熟悉项目过程中

刑天机器人本身 背景知识&#xff1a; 刑天与地平线的关系、旭日派的关系&#xff1f; 地平线是一家公司&#xff0c;刑天机器人是他们家的一款产品&#xff0c;是硬件&#xff0c;旭日派也是他们家的一款产品&#xff0c;用作刑天机器人的大脑。 刑天机器人的源代码能安装…

【网络安全无小事】汽车网络安全:只有开局,没有尽头,聚光向前,安全到达

“ 汽车网络安全: 只有开局&#xff0c;没有尽头&#xff0c;聚光向前&#xff0c;安全到达。” 01 — 引言 汽车一直以来都将速度、激情、男性荷尔蒙和阳刚气息联系在一起&#xff0c;这种联系似乎已经成为了文化符号。然而&#xff0c;作为一名安全从业者&#xff0c;我时常担…

汽车电子 -- Bin、Hex、Srec、s19、elf等文件的区别

一、烧写文件 烧写文件有很多种格式&#xff0c;比如bin、Hex、s19等。下面对这几种文件格式做一下介绍。 1、bin文件 参看&#xff1a;【嵌入式烧录/刷写文件】-3.1-详解二进制Bin格式文件 bin是binary的缩写&#xff0c;翻译为“二进制”。binary file 二进制文件是一个…

idea提交代码冲突后,代码意外消失解决办法

敲了大半天的代码&#xff0c;解决冲突后&#xff0c;直接消失了当时慌的一批CCCCC 右击项目Local History ----show History 找到最近提交的内容右击选择Revert,代码全回来了

python+request接口自动化框架

目前我们需要考虑的是如何实现关键字驱动实现接口自动化输出&#xff0c;通过关键字的封装实现一定意义上的脚本与用例的脱离&#xff01; robot framework 的安装不过多说明&#xff0c;网上资料比较太多~ 实例&#xff1a;&#xff01;&#xff01;&#xff01;&#xff01…

matlab simulink PMSM永磁电机DTC控制

1、内容简介 略 10-可以交流、咨询、答疑 2、内容说明 PMSM永磁电机DTC控制 PMSM、永磁电机、DTC控制 传 是 &#xff0c;它的工作原理是&#xff1a;首先设定好运行期望值和滞环的容差值&#xff0c;然后把电机 到的差值 号输出&#xff0c;可以 示需要减小 大输出…

​LeetCode解法汇总1465. 切割后面积最大的蛋糕

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 矩形蛋糕的…

ChatGLM系列七:LangChain+ChatGLM-6B

背景介绍 在开发过程中&#xff0c;我们经常会遇到需要构建基于本地知识库的问答系统的问题。这种系统需要能够根据用户提供的问题&#xff0c;在本地的知识库中查找并返回相关答案。然而&#xff0c;要实现这样的功能并不容易&#xff0c;涉及到语言模型的选择、知识库的管理…

OpenCV+OpenCvSharp实现图片特征向量提取与相似度计算

图片特征向量是一种用于描述图片内容的数学表示&#xff0c;它可以反映图片的颜色、纹理、形状等信息。图片特征向量可以用于做很多事情&#xff0c;比如图片检索、分类、识别等。 本文将介绍图片特征向量的提取以及相似度的计算&#xff0c;并使用C#来实现它们。 文章开始前&a…

论文阅读——BART

Arxiv: https://arxiv.org/abs/1910.13461 一个去噪自编码器的预训练序列到序列的模型。是一个结合了双向和自回归transformers的模型。 预训练分为两个阶段&#xff1a;任意噪声函数破坏文本和序列模型重建原始文本 一、模型 input&#xff1a;被破坏的文本-->bidirecti…

折磨的Ts

先看下官网 这里的withDefault是给props设置默认值的 由于props传入了个函数在设置默认值的时候不知道怎么设置了 解决办法&#xff1a;直接不设置了。也不写了。

JAVA毕业设计106—基于Java+Springboot的外卖系统(源码+数据库)

基于JavaSpringboot的外卖系统(源码数据库)106 一、系统介绍 本系统分为用户端和管理端角色 前台用户功能&#xff1a; 登录、菜品浏览&#xff0c;口味选择&#xff0c;加入购物车&#xff0c;地址管理&#xff0c;提交订单。 管理后台&#xff1a; 登录&#xff0c;员工管…