前言
数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷。
也因如此,它作为博主大二上学期最重要的必修课出现了。由于大家对于上学期C++系列博文的支持,我打算将这门课的笔记也写作系列博文,既用于整理、消化,也用于同各位交流、展示数据结构的美。
此系列文章,将会分成两条主线,一条“数据结构基础”,一条“数据结构拓展”。“数据结构基础”主要以记录课上内容为主,“拓展”则是以课上内容为基础的更加高深的数据结构或相关应用知识。
欢迎关注博主,一起交流、学习、进步,往期的文章将会放在文末。
话说在前面的章节,我们介绍了一种工程上常用的AOV网络,他可以清晰的表示工程中每项活动的先后顺序,并计算出满足要求的活动规划,也就是拓扑排序。
这一节,我们将继续研究这类网络。
AOE网络
在一般工程项目中,我们不仅会关心每项活动的先后次序,更会关心活动之间更加严格的时间约束关系。
不妨改造AOV网络的有向无环图,在此基础上引入边权,使其成为有向有权无环图。
以一条有向边代替一个活动,边权为其进行的时间长度,边的起点表示该任务的开始,终点表示该任务的完成。从点的角度来看,出边表示一个活动开始的状态,也称为一个事件,入边表示一个任务的完成。
这样的图被称作AOE网络( A c t i v i t y O n E d g e N e t w o r k Activity\ On\ Edge\ Network Activity On Edge Network)。如果比较他和AOV网络的名称,不难发现他将关注的重点从顶点变成了边,这也将是两个网络处理问题和算法不同之处的来源。
在工程中由于一般只有一个起始点和一个完成点,所以在正常的情况下,一张AOE网络中也只有一个源点和汇点。其中源点入度为0,汇点出度为0。如下图:
在AOE网络中,我们关心的问题不在仅仅是任务进行的先后顺序,更包括了每个任务乃至整个工程项目的计划完成时间。一般来说,一些活动能够同时开工,他们会从同一顶点出发。有些状态必须等到若干前置工作的完成,这些任务会汇集到一个顶点处。
画出工程对应的AOE网络可以使我们得出:
- 完成整个工程至少需要多少时间
- 为缩短完成工程所需要的时间,应当加快那些活动
- 为了不延误整个工程,哪些活动不得延期,那些活动可以适当延期
关键路径
根据上文的叙述,不难发现整个工程完成的最短时间,取决于从源点到汇点的最长路径的长度。我们将这个路径定义为该网络的关键路径。上文图中的关键路径就如下所示:
在关键路径中的任何一环不能按时完成,就会延误整个项目的进度。如在上图中,标红的路径中任意一条路径的长度增加都会使得源点到汇点的最长路径增加。而增加其他边则不一定。
需要注意的是,关键路径也有可能存在多条,定义中并没有限制一张AOE网络只能有一条关键路径。如将上图中的权值稍作修改,就会出现多条关键路径:
在关键路径上所有的活动都是关键活动。(需要注意的是在AOE网络中,活动是边而不是顶点)
多条关键路径的来源往往就在于一些状态之间可能存在着不同的活动路径,他们的时间加和相同(就像上图中的状态15之间存在两条路径)。而不同的路径在经过相同的顶点时遵循着乘法原理(上图为 1 × 2 1\times2 1×2)
算法设计
有了对关键路径的定义和分析,下面的重点就是如何从AOE网络中寻找关键路径。
这里介绍一种基于拓扑排序的算法思路:
- 将源点加入队列,所有顶点默认最长距离为0
- 每次出队队首顶点,使用该顶点更新出边终点。若经过该边到达彼点的累计路径长度大于原有顶点距离,更新最长距离并记录当前顶点为其前驱
- 将该顶点“删除”(所有出边终点入度-1),如果有顶点入度为0,则加入队列
- 重复23过程,直至队列为空
该算法基于拓扑排序算法,改进也并不大,只是在记录顶点的入度和出度之余多记录了源点到他的最长路径。
代码实现
路径长度
与关键路径相关的需求有很多,不妨先拟定一个需求,仅求出关键路径的长度:
给定一张有向有权无环的AOE网络,求出其源点到汇点的关键路径长度
输入格式:
第一行包含两个整数n,m,表示顶点的个数和边的条数
接下来m行,每行3个整数表示每条边的起点,终点和权值
存储格式采用邻接表,顶点信息定义如下:
struct Edge{
int vertex;
Edge * next;
int len;
Edge(int vertex,Edge * next){
this->vertex = vertex;
this->next = next;
this->len = len;
}
};
Edge *head[N];//边链表头结点数组
int id[N];//入度
int od[N];//出度
int dis[N];//顶点到源点的最长距离
代码实现如下:
void link(int x,int y,int len){
Edge * edge = new Edge(y,head[x],len);
head[x] = edge;
}
int queue[N];//队列
int main(){
int n,m;
cin <