拓扑排序、关键路径(AOV、AOE网)

拓扑排序(AOV网)

相关知识

在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,一个工程常被分为多个小的子工程,这些子工程被称为活动(Activity)。 在有向图中若以顶点表示活动,有向边表示活动之间的先后关系,这样的图简称为AOV网。 图中的拓扑排序算法(Topological Sort)可以给出一个活动的合法序列。

拓扑序列  设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1, v2, …, vn称为一个拓扑序列,当且仅当满足下列条件:若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在顶点vj之前。

拓扑排序  对一个有向图构造拓扑序列的过程称为拓扑排序 。

图的存储结构

采用邻接表存储 ,在顶点表中增加一个入度域 in。

栈S:存储所有无前驱的顶点。

拓扑排序算法——伪代码

  1. 栈S初始化;累加器count初始化;
  2. 扫描顶点表,将没有前驱的顶点压栈;
  3. 当栈S非空时循环 3.1 vj=退出栈顶元素;输出vj;累加器加1; 3.2 将顶点vj的各个邻接点的入度减1; 3.3 将新的入度为0的顶点入栈;
  4. if (count<vertexNum) 输出有回路信息;

输入输出

第一行输入顶点个数和边的个数 后续行输入边依附的两个顶点的数字编号(测试用例的编号都从0开始) 输出拓扑排序的结果。如果存在环,输出拓扑排序的部分结果,再输出“图中存在环,无法拓扑排序”。

测试数据1

拓扑排序测试用例1

输入

7 10

0 2

0 3

1 3

1 5

2 4

3 4

3 5

3 6

4 6

5 6

输出

1 0 2 3 4 5 6

测试数据2

拓扑排序测试用例2

输入

6 8

0 2

0 3

1 3

1 5

2 4

3 4

4 5

5 3

输出

1 0 2 图中存在环,无法拓扑排序

代码

#include<iostream>
#include<stack>
#include<cstring>
#define MAX 100
using namespace std;
typedef struct ArcNode          //边结点
{int adjvex;                 //顶点下标ArcNode *next;
} ArcNode;
typedef struct
{int in;                     //in是入度int vertex;              //顶点信息ArcNode *firstEdge;
} vertexNode,VertexNode[MAX];class ALGraph
{
private:int vertexNum,arcNum;   //顶点数,边数VertexNode adjList;     //顶点数组stack<vertexNode> s;    //栈int count=0;            //计数
public:ALGraph(int v[],int n,int e);void TopologicalSort(); //拓扑排序
};
void ALGraph::TopologicalSort()
{for(int i=0; i<vertexNum; i++)  //in为0则压栈{if(adjList[i].in==0){s.push(adjList[i]);}}while(!s.empty())               //循环终止条件:栈为空{vertexNode v=s.top();       //弹栈输出s.pop();cout<<v.vertex<<" ";count++;                    //计数加一ArcNode *a=v.firstEdge;while(a)                    //对弹出的结点遍历,所有遍历过的结点的in-1{adjList[a->adjvex].in--;int tmp=adjList[a->adjvex].in;if(tmp==0)              //如果某结点的in变为0,则将其压栈{s.push(adjList[a->adjvex])    ;}a=a->next;}}if(count<vertexNum)cout<<"图中存在环,无法拓扑排序";//如果计数小于顶点数则说明有环
}
ALGraph::ALGraph(int v[],int n,int e)    //构造函数
{vertexNum=n;arcNum=e;for(int i=0; i<vertexNum; i++)          //顶点初始化{adjList[i].in=0;adjList[i].vertex=v[i];adjList[i].firstEdge=NULL;}ArcNode *s;int vi,vj;for(int i=0; i<arcNum; i++){s=new ArcNode;cin>>vi>>vj;s->adjvex=vj;s->next=adjList[vi].firstEdge;      //头插法adjList[vi].firstEdge=s;adjList[vj].in++;                   //入度加一}
}
int main()
{int n,e;cin>>n>>e;int v[MAX];for(int i=0; i<n; i++){v[i]=i;}ALGraph algraph(v,n,e);algraph.TopologicalSort();return 0;
}

关键路径(AOE网)

AOE网

在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间,称这样的有向图叫做边表示活动的网,简称AOE网。AOE网中没有入边的顶点称为始点(或源点),没有出边的顶点称为终点(或汇点)。

AOE网的性质

⑴ 只有在某顶点所代表的事件发生后,从该顶点出发的各活动才能开始; ⑵ 只有在进入某顶点的各活动都结束,该顶点所代表的事件才能发生。

关键路径

在AOE网中,从始点到终点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。

关键活动

关键路径上的活动称为关键活动。

关键路径可能不只一条,重要的是找到关键活动

关键活动的计算方法

首先计算以下与关键活动有关的量:

⑴ 事件的最早发生时间ve[k]

⑵ 事件的最迟发生时间vl[k]

⑶ 活动的最早开始时间ee[i]

⑷ 活动的最晚开始时间el[i]

最后计算各个活动的时间余量 el[k] - ee[k],时间余量为0者即为关键活动。

关键路径算法

⑴ 事件的最早发生时间ve[k] ve[前驱结点]+边 最大的一组,顺着求

⑵ 事件的最迟发生时间vl[k] vl[后继结点]-边 最小的一组, 逆着求

⑶ 活动的最早开始时间ee[i] 边的起点的ve

⑷ 活动的最晚开始时间el[i] 边的终点的vl-边长

关键路径算法伪码

step1、求ve

调用拓扑排序函数,创建数组resultStack存储拓扑排序结果序列(存储下标)。 一边拓扑排序,一边计算ve。

遍历所有的出边,如果ve[前驱/起点]+边的权值>ve[后继/终点],更新ve的值

step2、求vl

把resultTop数组看成栈,汇点先出栈 初始化vl数组为即汇点的ve值,这个ve值是最大的

依次弹出resultTop数组的元素,按拓扑逆序求个顶点的vl值

当栈不为空时

resultStack出栈。inVex为起点,访问inVex为起点的所有终点。

取一个终点,计算起点的vl值。

如果vl[后继结点]-边<vl[前驱结点]

vl[inVex] = vl[后继结点]-边;

step3、从上往下扫描顶点表,处理每个顶点的边表,计算ee和el的值

活动最早开始时间ee,与当前边起点的ve值相等

活动最晚开始时间el,为当前边终点的vl值-边的权值

如果ee==el,输出关键路径

题目要求

通过求关键路径,计算完成整个工程至少需要多少时间

输入输出

第一行输入有向图的顶点个数和边的个数

下面多行输入边的起点和终点的编号和边的权值

输出第一行为所有关键路径的边。如果不能求关键路径,则输出“存在环,无关键路径。”

测试数据1

,

输入 9 11

0 1 6

0 2 4

0 3 5

1 4 1

2 4 1

3 5 2

4 6 9

4 7 7

5 7 4

6 8 2

7 8 4

输出

<0,1>6 <1,4>1 <4,7>7 <4,6>9 <6,8>2 <7,8>4

测试数据2

关键路径测试用例2

输入: 4 5

0 1 2

1 2 3

2 0 4

0 3 5

2 3 7

输出:

存在环,无关键路径。

代码

#include <iostream>
#include <stdio.h>
using namespace std;
const int MAX = 30;
struct ArcNode
{int weight;int adjvex;ArcNode* next;
};
struct VertexNode
{int in; //入度char vertex;  //图的顶点ArcNode* firstEdge;  //指向第一个边表
};
class ALGraph
{
private:VertexNode* adjList; //邻接表int vertexNum, arcNum;int* ve, * vl; //分别为事件最早发生时间的数组,事件最迟发生时间的数组
public:ALGraph(char v[], int n, int e);~ALGraph();void inputEdges();bool setEdge(int vi, int vj, int weight); //设置边void display();bool Topological(int result[], int& count); //拓扑排序bool GriticalPath();  //求关键路径
};
ALGraph::ALGraph(char v[], int n, int e)
{vertexNum = n;arcNum = e;adjList = new VertexNode[vertexNum]; //创建for (int i = 0; i < vertexNum; i++){adjList[i].in = 0;adjList[i].vertex = v[i];adjList[i].firstEdge = NULL;}ve = new int[vertexNum];vl = new int[vertexNum];
}void ALGraph::inputEdges()
{//cout << "输入" << endl;for (int i = 0; i < arcNum; i++){int vi, vj, weight;cin >> vi >> vj >> weight;if (!setEdge(vi, vj, weight)){cout << "超过范围,重新输入" << endl;i--;}}
}
bool ALGraph::setEdge(int vi, int vj, int weight)
{ArcNode* s;if (vi >= 0 && vi < vertexNum && vj >= 0 && vj < vertexNum && vi != vj){//创建一个边结点vjs = new ArcNode;s->adjvex = vj;s->weight = weight;// 把边结点vj插入顶点表的vi项的邻接表中,成为第一个节点s->next = adjList[vi].firstEdge;adjList[vi].firstEdge = s;// 入度+1adjList[vj].in++;return true;}else{return false;}
}
ALGraph::~ALGraph()
{ArcNode* p, * pre;// 顶点表指向所有边的结点删除for (int i = 0; i < vertexNum; i++){p = adjList[i].firstEdge;adjList[i].firstEdge = NULL;while (p){pre = p;p = p->next;delete pre;}}delete[] adjList;delete[] ve;delete[] vl;
}
bool ALGraph::Topological(int result[], int& count)
{int stack[MAX];int top = -1;int inVex;int outVex;ArcNode* p;for (int i = 0; i < vertexNum; i++){ve[i] = 0;}for (int i = 0; i < vertexNum; i++){if (adjList[i].in == 0){stack[++top] = i;}}count = 0;  //统计有多少个顶点被处理过了while (top != -1){inVex = stack[top--]; //出栈result[count] = inVex; //存入数组count++; //下一次存储//找出当前处理的顶点的所有边p = adjList[inVex].firstEdge;while (p)  //扫描边表 “删掉”边{outVex = p->adjvex; //获取当前边表的终点值adjList[outVex].in--; //入度减1if (adjList[outVex].in == 0) //把入度为0的压入堆栈stack[++top] = outVex;if (ve[inVex] + p->weight > ve[outVex]) //找到最大的时间ve[outVex] = ve[inVex] + p->weight;p = p->next;}}// 判断排序是否正确if (count == vertexNum) //该拓扑排序是无环图{return true;}elsereturn false;}
bool ALGraph::GriticalPath()
{int resultStack[MAX]; //存储拓扑排序的序列的栈int resultTop;  //栈顶指针ArcNode* p;int i, count;int inVex, outVex;if (!Topological(resultStack, count)){return false;}resultTop = count - 1; //指向栈顶inVex = resultStack[resultTop--]; //栈顶元素出栈//求Vl数组,倒序求最小值//初始化for (i = 0; i < vertexNum; i++){vl[i] = ve[inVex];}while (resultTop != -1){inVex = resultStack[resultTop--];p = adjList[inVex].firstEdge;while (p){outVex = p->adjvex;if (vl[inVex] > vl[outVex] - p->weight){vl[inVex] = vl[outVex] - p->weight;}p = p->next;}}for (inVex = 0; inVex < vertexNum; inVex++)//从上往下扫描边表{p = adjList[inVex].firstEdge;while (p){outVex = p->adjvex;int weight = p->weight;int ee = ve[inVex]; //活动的最早开始时间int el = vl[outVex] - weight;  //活动的最晚开始时间if (ee == el){cout << "<" << inVex << "," << outVex << ">" << weight << " ";}p = p->next;}}return true;
}
int main()
{char vertex[MAX];int num, edge;cin >> num >> edge;for (int i = 0; i < num; i++){vertex[i] = i + '0';}ALGraph graph(vertex, num, edge);graph.inputEdges();//graph.display();if (!graph.GriticalPath()){cout << "存在环,无关键路径。";}// 自动调用析构函数释放程序return 0;
}

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

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

相关文章

Sentence Transformers x SwanLab:可视化Embedding训练

Sentence Transformers(又名SBERT)是访问、使用和训练文本和图像嵌入&#xff08;Embedding&#xff09;模型的Python库。 你可以使用Sentence Transformers快速进行模型训练&#xff0c;同时使用SwanLab进行实验跟踪与可视化。 1. 引入SwanLabCallback from swanlab.integra…

lwip中server和client的socket、地址和端口号

1、server的socket通过lwip_socket建立&#xff1a; server_sd lwip_socket(AF_INET, SOCK_STREAM, 0);2、client的socket在监听到连接后建立&#xff1a; client_sd lwip_accept(server_sd, (struct sockaddr *)&client_addr_port, (socklen_t *)&size);3、server…

【STM32】基于RTOS的CAN异步接收转发数据

文章目录 前言实现 前言 现象&#xff1a;全局变量在 CAN 中断中存储数据&#xff0c;并设置同步标志&#xff0c;在主程序中检测标志后&#xff0c;打包并转发 CAN 数据&#xff0c;会出现 CAN 数据错乱 现象分析&#xff1a;CAN 数据打包处理过程中&#xff0c;新的数据到来…

AI时代新爬虫:网站自动转LLM数据,firecrawl深度玩法解读

在大模型的时代&#xff0c;爬虫技术也有了很多新的发展&#xff0c;最近出现了专门针对大模型来提取网站信息的爬虫&#xff0c;一键将网页内容转换为LLM-ready的数据。今天我们介绍其中的开源热门代表&#xff1a;firecrawl。 firecrawl 是什么 FireCrawl是一款创新的爬虫工…

《2023-2024中国数据资产发展研究报告》

中国电子信息产业发展研究院发布《2023-2024中国数据资产发展研究报告》&#xff08;下称《报告》&#xff09;&#xff0c;紧跟国家战略部署&#xff0c;调研国内数据资产发展现状&#xff0c;掌握数据价值实现路径&#xff0c;助力释放数字经济新动能。 《报告》从数据资产相…

这家来自内蒙古的物流企业,用另一种方式减碳超500吨

2016年&#xff0c;多蒙德实业集团整合旗下物流及运销板块&#xff0c;组建成立了内蒙古多蒙德科技有限公司&#xff08;以下简称“多蒙德”&#xff09;&#xff0c;整合互联网、大数据及智慧物流为一体&#xff0c;自主研发多蒙达网络货运平台及多个供应链智慧系统&#xff0…

pytest + yaml 框架 -62.支持yaml和json2种格式用例

前言 v1.5.7版本开始新增json格式用例支持,本次版本改动内容 1.支持 .json 文件用例2.优化日志中文件后缀名称.yml .yaml .json3.ruamel.yaml 版本兼容0.18.6yaml 格式用例 yaml 格式用例示例,test_a.yml test_demo:name: postrequest:method: POSTurl: http://httpbin.or…

24年下教资笔试报名照片要求及处理方法

24年下教资笔试报名照片要求及处理方法

关闭kylin(麒麟)系统的安全认证(烦人的安全认证)

打开grub sudo vim /etc/default/grup修改安全认证选项 增加12行&#xff0c;把13行注释掉 保存更改, 然后执行下面的命令&#xff1a; sudo sync sudo reboot重启成功后&#xff0c;就关闭了安全认证了~~~~~。 总体来讲&#xff0c;kylin还是基于ubuntu的内核的&#xff0c;…

文章解读与仿真程序复现思路——电工技术学报EI\CSCD\北大核心《考虑源网储协同配合下的移动式波浪能发电平台并网优化调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

PostgreSQL基础知识

PostgreSQL简介 PostgreSQL是一个强大的开源对象关系数据库系统&#xff0c;它使用并扩展了SQL语言&#xff0c;并结合了许多功能&#xff0c;可以安全地存储和扩展最复杂的数据工作负载。PostgreSQL的起源可以追溯到1986年&#xff0c;是加州大学伯克利分校POSTGRES项目的一部…

细说ARM MCU的串口接收数据的实现过程

目录 一、硬件及工程 1、硬件 2、软件目的 3、创建.ioc工程 二、 代码修改 1、串口初始化函数MX_USART2_UART_Init() &#xff08;1&#xff09;MX_USART2_UART_Init()串口参数初始化函数 &#xff08;2&#xff09;HAL_UART_MspInit()串口功能模块初始化函数 2、串口…

深入解析Prometheus:强大的开源监控与告警系统

目录 引言 一、运维监控平台的设计思路 &#xff08;一&#xff09;设计思路 1.数据收集模块 2.数据提取模块 3.监控告警模块 &#xff08;二&#xff09;监控平台层级 二、Prometheus简介 &#xff08;一&#xff09;基本介绍 &#xff08;二&#xff09;核心特征 …

Wake Lock API:保持设备唤醒的利器

随着移动设备和 Web 应用的普及&#xff0c;如何有效管理设备的电源成为开发者们关注的一个重要问题。Wake Lock API 是一种强大的工具&#xff0c;它允许 Web 应用请求设备保持唤醒状态&#xff0c;从而防止屏幕变暗或设备进入睡眠模式。在这篇文章中&#xff0c;我们将详细介…

vue+elementUI实现在表格中添加输入框并校验的功能

背景&#xff1a; vue2elmui 需求&#xff1a; 需要在一个table中添加若干个输入框&#xff0c;并且在提交时需要添加校验 思路&#xff1a; 当需要校验的时候可以考虑添加form表单来触发校验&#xff0c;因此需要在table外面套一层form表单&#xff0c;表单的属性就是ref…

救命!接手了一个老项目,见到了从业10年以来最烂的代码!

后台回复“书籍”&#xff0c;免费领取《程序员书籍资料一份》 后台回复“5000”&#xff0c;免费领取面试技术学习资料一份 在程序员这个行业从业快10年了&#xff0c;每过几个月回头看看自己写的代码&#xff0c;都会觉得写的也太烂了&#xff0c;不敢想象是自己之前写的。…

2024黄河流域比赛的复现

目录 WEB [GKCTF 2021]easynode unser 知识点 WEB 根据此题先复现[GKCTF 2021]easynode这个题&#xff0c;这两个题类似 [GKCTF 2021]easynode 1.打开页面发现是登录页面&#xff0c;找到源文件里面的代码&#xff0c;分析如何进行登录&#xff0c;发现经过safeQuery()函…

【C++】基础复习 运算符|算数、关系、逻辑、赋值、位运算符

文章目录 1 算术运算符1.1 整数除法1.2 浮点数除法1.3 混合运算 2 关系运算符3 逻辑运算符4 赋值运算符5 位运算符6 条件运算符&#xff08;三元运算符&#xff09;7 其他重点补充 1 算术运算符 算术运算符用于执行基本的数学运算。 运算符描述示例结果加法a b两数相加-减法…

2024年6月13日随笔

现在已经是6月14日了&#xff0c;十二点多了&#xff0c;宿舍依旧很吵&#xff0c;根本睡不着&#xff0c;索性下来写篇随笔&#xff0c;已经好久没写过随笔了&#xff0c;最近这两个月都很忙&#xff0c;双学位的考试&#xff0c;两个课程设计&#xff0c;还有其他一些杂七杂八…

Docker使用心得

Docker使用心得 最近使用Docker比较频繁&#xff0c;特此想记录一下&#xff0c;方便后续查找。 Docker常用命令Docker如何配置使用GPU环境&#xff1f;如何使用Dockerfile构建镜像&#xff1f;如何使用docker compose 实例化容器&#xff1f; Docker如何配置使用GPU环境 参…