图的知识点补充(AOE网络的关键路径)

2023-11-02 08:48

本文主要是介绍图的知识点补充(AOE网络的关键路径),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图。
    AOE(Activity On Edge)网:顾名思义,用边表示活动的网,当然它也是DAG。与AOV不同,活动都表示在了边上,如下图所示:
                                    
    如上所示,共有11项活动(11条边),9个事件(9个顶点)。整个工程只有一个开始点和一个完成点。即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)。
    关键路径:是从开始点到完成点的最长路径的长度。路径的长度是边上活动耗费的时间。如上图所示,1 到2 到 5到7到9是关键路径(关键路径不止一条,请输出字典序最小的),权值的和为18。

Input

    这里有多组数据,保证不超过10组,保证只有一个源点和汇点。输入一个顶点数n(2<=n<=10000),边数m(1<=m <=50000),接下来m行,输入起点sv,终点ev,权值w(1<=sv,ev<=n,sv != ev,1<=w <=20)。数据保证图连通。

Output

    关键路径的权值和,并且从源点输出关键路径上的路径(如果有多条,请输出字典序最小的)。

Sample Input

9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
8 9 4
7 9 2

Sample Output

18
1 2
2 5
5 7
7 9

思路:这个题目实际上考查的是AOE网络的关键路径问题。

1、先了解一下AOE网络的关键路径的原理,请参考:经典算法之关键路径

 

2、具体计算步骤再细致地理一下:

Ev[i]表示事件i的最早出现时间,E[i]表示活动i的最早出现时间,Lv[i]表示事件i的最迟出现时间,L[i]表示活动i的最迟出现时间。

以j顶点指向k顶点为例:

(1)从源点开始向汇点递推,根据Ev[k]=max(Ev[j]+w[j,k]),求出Ev数组。其中的最大值Ev[n]是汇点的最早开始时间。

(2)因为汇点就是结束点,其最迟出现时间与最早出现时间相同,即Ev[n]=Lv[n]。将(1)中排好序的事件,从汇点开始向源点递推,根据Lv[j]=min(Lv[k]-w[j,k]),求出Lv数组。

(3)活动的最早开始时间与事件的最早开始时间是一样的,求出E数组,E=Ev.

(4)j顶点指向k顶点,中间经历活动aj, 根据L[j]=Lv[k]-(活动j所需的时间),求出L数组。

(5)E[i]=L[i]的活动,就是关键活动,由这些活动组成的路径就是关键路径。

(6)有可能关键路径不止一条,那么这时候可以将re按照字典序排序,如果后一条路径的起始点,与前一条路径的结束点不一致,即x[i][0]!=x[i-1][1],删后面的元素。

 

3、设计算法所用到的数据结构:

输入:m和n两个整数,一个二维数组G(G的长度应该和m是一样的),每个数组的元素是一个长度为3的一维数组,分别存一条边的两个顶点和权重。

输出:一个二维数组,每个数组的元素是一个长度为2的一维数组,分别存关键路径上每一条边的两个顶点。

(1)建一个长度为n(顶点数)的二维数组Event,每个数组的元素是一个长度为2的一维数组,分别存每个事件的Ev[i]和Lv[i]。

(2)建一个长度为m(边数)的二维数组Acti,每个数组的元素是一个长度为2的一维数组,分别存每个活动的E[i]和L[i]。

 

4、代码实现:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <unordered_map>
#include <sstream>
#include <stack>
#include <unordered_set>
#include <math.h>
using namespace std;vector<vector<int> > AOE(vector<vector<int> > &g, int n, int m) {//建一个长度为n(顶点数)的二维数组Event,每个数组的元素是一个长度为2的一维数组,分别存每个事件的Ev[i]和Lv[i]vector<vector<int> >Event(n + 1, vector<int>(2));//建一个长度为m(边数)的二维数组Acti,每个数组的元素是一个长度为2的一维数组,分别存每个活动的E[i]和L[i]vector<vector<int> >Acti(m, vector<int>(2));//把原图中的信息存在一个哈希表里面,方便查找unordered_map<int, unordered_map<int, int> >mm;for (auto x : g){mm[x[0]][x[1]] = x[2];}//从源点开始向汇点递推,根据Ev[k]=max(Ev[j]+w[j,k]),求出Ev数组,也就是Event数组每个元素x的x[0]for (int i = 1; i <= n; ++i){for (int j = 1; j < i; ++j){if (mm[j].count(i)){Event[i][0] = max(Event[i][0], Event[j][0] + mm[j][i]);}}}cout << endl << endl << "Event[i][0]:" << endl;for (auto x : Event){cout << x[0] << " ";}cout << endl;//从汇点开始向源点递推,根据Lv[j]=min(Lv[k]-w[j,k]),求出Lv数组,也就是Event数组每个元素x的x[1]for (auto &x : Event){x[1] = Event.back()[0];}for (int i = n - 1; i >=1; --i){for (int j = i + 1; j <= n; ++j){if (mm[i].count(j)){Event[i][1] = min(Event[i][1], Event[j][1] - mm[i][j]);}}}cout << endl << endl << "Event[i][1]:" << endl;for (auto x : Event){cout << x[1] << " ";}cout << endl;//活动的最早开始时间与事件的最早开始时间是一样的,求出E数组,也就是Acti数组每个元素x的x[0]for (int i = 0; i < m; ++i){Acti[i][0] = Event[g[i][0]][0];}cout << endl << endl << "Acti[i][0]:" << endl;for (auto x : Acti){cout << x[0] << " ";}cout << endl;//j顶点指向k顶点,中间经历活动aj, 根据L[j]=Lv[k]-(活动j所需的时间),求出L数组,也就是Acti数组每个元素x的x[1]for (int i = m - 1; i >= 0; --i){Acti[i][1] = Event[g[i][1]][1] - g[i][2];}cout << endl << endl << "Acti[i][1]:" << endl;for (auto x : Acti){cout << x[1] << " ";}cout << endl;vector<vector<int> >re;//找出Acti数组中元素x的x[0]=x[1]的活动,就是关键活动for (int i = 0; i < Acti.size(); ++i){if (Acti[i][0] == Acti[i][1]){re.push_back({ g[i][0], g[i][1] });}}//题目中要求:如果有多条,请输出字典序最小的,所以还需要做点小处理:将re按照字典序排序,如果x[i][0]!=x[i-1][1],删后面的元素sort(re.begin(), re.end());for (int i = 1; i < re.size(); ++i){if (re[i][0] != re[i - 1][1]){re.erase(re.begin() + i);}}return re;
}int main()
{vector<vector<int> > g = { { 1, 2, 6 }, { 1, 3, 4 }, { 1, 4, 5 }, { 2, 5, 1 }, { 3, 5, 1 },{ 4, 6, 2 }, { 5, 7, 9 }, { 5, 8, 7 }, { 6, 8, 4 }, { 8, 9, 4 }, { 7, 9, 2 } };vector<vector<int> > re = AOE(g, 9, 11);cout << endl << endl << "关键路径:" << endl;for (auto x : re){for (auto y : x){cout << y << " ";}cout << endl;}return 0;
}

来一个只写函数的干净简洁版本:

vector<vector<int> > AOE(vector<vector<int> > &g, int n, int m) {//建一个长度为n(顶点数)的二维数组Event,每个数组的元素是一个长度为2的一维数组,分别存每个事件的Ev[i]和Lv[i]vector<vector<int> >Event(n + 1, vector<int>(2));//建一个长度为m(边数)的二维数组Acti,每个数组的元素是一个长度为2的一维数组,分别存每个活动的E[i]和L[i]vector<vector<int> >Acti(m, vector<int>(2));//把原图中的信息存在一个哈希表里面,方便查找unordered_map<int, unordered_map<int, int> >mm;for (auto x : g){mm[x[0]][x[1]] = x[2];}//从源点开始向汇点递推,根据Ev[k]=max(Ev[j]+w[j,k]),求出Ev数组,也就是Event数组每个元素x的x[0]for (int i = 1; i <= n; ++i){for (int j = 1; j < i; ++j){if (mm[j].count(i)){Event[i][0] = max(Event[i][0], Event[j][0] + mm[j][i]);}}}//从汇点开始向源点递推,根据Lv[j]=min(Lv[k]-w[j,k]),求出Lv数组,也就是Event数组每个元素x的x[1]for (auto &x : Event){x[1] = Event.back()[0];}for (int i = n - 1; i >=1; --i){for (int j = i + 1; j <= n; ++j){if (mm[i].count(j)){Event[i][1] = min(Event[i][1], Event[j][1] - mm[i][j]);}}}//活动的最早开始时间与事件的最早开始时间是一样的,求出E数组,也就是Acti数组每个元素x的x[0]for (int i = 0; i < m; ++i){Acti[i][0] = Event[g[i][0]][0];}//j顶点指向k顶点,中间经历活动aj, 根据L[j]=Lv[k]-(活动j所需的时间),求出L数组,也就是Acti数组每个元素x的x[1]for (int i = m - 1; i >= 0; --i){Acti[i][1] = Event[g[i][1]][1] - g[i][2];}vector<vector<int> >re;//找出Acti数组中元素x的x[0]=x[1]的活动,就是关键活动for (int i = 0; i < Acti.size(); ++i){if (Acti[i][0] == Acti[i][1]){re.push_back({ g[i][0], g[i][1] });}}//题目中要求:如果有多条,请输出字典序最小的,所以还需要做点小处理:将re按照字典序排序,如果x[i][0]!=x[i-1][1],删后面的元素sort(re.begin(), re.end());for (int i = 1; i < re.size(); ++i){if (re[i][0] != re[i - 1][1]){re.erase(re.begin() + i);}}return re;
}

5、输出结果截图

这篇关于图的知识点补充(AOE网络的关键路径)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/329465

相关文章

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

python获取当前文件和目录路径的方法详解

《python获取当前文件和目录路径的方法详解》:本文主要介绍Python中获取当前文件路径和目录的方法,包括使用__file__关键字、os.path.abspath、os.path.realp... 目录1、获取当前文件路径2、获取当前文件所在目录3、os.path.abspath和os.path.re

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

hdu2544(单源最短路径)

模板题: //题意:求1到n的最短路径,模板题#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#i

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了