图的知识点补充(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

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想

Python中Windows和macOS文件路径格式不一致的解决方法

《Python中Windows和macOS文件路径格式不一致的解决方法》在Python中,Windows和macOS的文件路径字符串格式不一致主要体现在路径分隔符上,这种差异可能导致跨平台代码在处理文... 目录方法 1:使用 os.path 模块方法 2:使用 pathlib 模块(推荐)方法 3:统一使

一文教你解决Python不支持中文路径的问题

《一文教你解决Python不支持中文路径的问题》Python是一种广泛使用的高级编程语言,然而在处理包含中文字符的文件路径时,Python有时会表现出一些不友好的行为,下面小编就来为大家介绍一下具体的... 目录问题背景解决方案1. 设置正确的文件编码2. 使用pathlib模块3. 转换路径为Unicod

前端知识点之Javascript选择输入框confirm用法

《前端知识点之Javascript选择输入框confirm用法》:本文主要介绍JavaScript中的confirm方法的基本用法、功能特点、注意事项及常见用途,文中通过代码介绍的非常详细,对大家... 目录1. 基本用法2. 功能特点①阻塞行为:confirm 对话框会阻塞脚本的执行,直到用户作出选择。②