图的关键路径,AOE,完整实现,C++描述
body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
1、事件的最早发生时间etv(earliest time of vertex):即顶点Vk的最早发生时间。 这个最大路径也相当于从始点到指定顶点Vk的最大路径长度。有etv[0]=0 etv[k]=max{etv[j]+len<Vj,Vk>} 2、事件的最晚发生时间ltv(latest time of vertex):即顶点Vk的最晚发生时间,也就是每个顶点对应的事件最晚需要开始的时间,超出此时间将会延误整个工期。 ltv[Vk]是指在不推迟整个工期的前提下,事件Vk允许的最晚发生时间。 ltv[Vn]=ve[Vn],Vn为图中最后一个顶点 ltv[Vk]=min{ltv[j]-len<Vk,j>} |
3、活动的最早开工时间ete(earliest time of edge):即弧ak的最早发生时间。
若活动ai是由弧<Vk,Vj>表示,则活动ai的最早开始时间应等于事件Vk的最早发生时间。有:ete[i]=etv[k]。
4、活动的最晚开工时间lte(latest time of edge):即弧ak的最晚发生时间,也就是不推迟工期的最晚开工时间。
若活动ai的最晚开始时间是指,在不推迟整个工期的前提下,ai必须开始的最晚时间。若ai由弧<Vk,Vj>表示,则ai的最晚开始时间要保证事件Vj的最迟发生时间不拖后。有:lte[i]=ltv[j]-<Vk,Vj>
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
初始etv全部为0,ltv为tev[9]
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
关键活动:ete = lte
a1 → a4 → a6 → a8 → a11 → a12 算法要首先确定有向图是不是AOV图,在确定的过程中要保存拓扑排序的顺序,最后初始化ltv数组要用到 |
/* AOE.h */
#ifndef __AOE_H__
#define __AOE_H__
#include"Graph.h"
#include<iostream>
#include<stack>
namespace meihao
{
//边表结点
typedef struct EdgeNode
{
int vertexIdx; //邻接点域,存放该结点在顶点表数组中的下标
int weight; //存放对应顶点到该边表结点的边上的权值
struct EdgeNode* next; //存放下一个边表结点的位置
}edgeNode,*pEdgeNode;
//顶点表结点
typedef struct VertexNode
{
int in; //顶点入度
int data; //顶点与,存放顶点数据信息
edgeNode* firstEdge;
}vertexNode,*pVertexNode;
void initDataStruct(const meihao::Graph& g,vertexNode*& vertexArr);
//根据图来初始化出我们要的顶点数组和对应的边表
int TopologicalSort_AOV(const meihao::Graph& g,vertexNode*& vertexArr,int* elv,stack<int>& AOV); //成功返回0,失败返回-1
//在这个过程中计算出elv,并保存这个拓扑排序的逆序
//这个拓扑排序的逆序后面求关键路径要用来计算ltv
void CriticalPath_AOE(const meihao::Graph& g);
};
#endif
/* data.txt */
10
0 1 2 3 4 5 6 7 8 9
0 3 4 0 0 0 0 0 0 0
0 0 0 5 6 0 0 0 0 0
0 0 0 8 0 7 0 0 0 0
0 0 0 0 3 0 0 0 0 0
0 0 0 0 0 0 9 4 0 0
0 0 0 0 0 0 0 6 0 0
0 0 0 0 0 0 0 0 0 2
0 0 0 0 0 0 0 0 5 0
0 0 0 0 0 0 0 0 0 3
0 0 0 0 0 0 0 0 0 0
/* testmain.cpp */
#include"Graph.h"
#include"AOE.h"
#include<iostream>
using namespace std;
int main()
{
meihao::Graph g("data.txt");
meihao::CriticalPath_AOE(g);
system("pause");
}
|
/* AOE.cpp */
#include"AOE.h"
namespace meihao
{
void initDataStruct(const meihao::Graph& g,vertexNode*& vertexArr)
{
int vertexNum = g.getGraphVertexNumber();
vertexArr = new VertexNode[vertexNum](); //顶点数组开辟空间
for(int idx=0;idx!=vertexNum;++idx)
{
vertexArr[idx].data = idx;
vertexArr[idx].in = g.getInputDegree(idx); //获取入度
vertexArr[idx].firstEdge = nullptr;
}
for(int idx=0;idx!=vertexNum;++idx)
{
for(int iidx=0;iidx!=vertexNum;++iidx)
{
if(0!=g.getGraphEdgeWeight(idx,iidx))
{
edgeNode* tmp = new edgeNode();
tmp->vertexIdx = iidx;
tmp->weight = g.getGraphEdgeWeight(idx,iidx);
tmp->next = vertexArr[idx].firstEdge;
vertexArr[idx].firstEdge = tmp;
}
}
}
}
int TopologicalSort_AOV(const meihao::Graph& g,vertexNode*& vertexArr,int* etv,stack<int>& AOV)
{
if(!AOV.empty())
return -1; //传递来的AOV必须是空的
stack<int> zeroInputDegreeVertex;
int vertexNum = g.getGraphVertexNumber();
vertexArr = nullptr;
initDataStruct(g,vertexArr);
//入度为0的顶点入栈
for(int idx=0;idx!=vertexNum;++idx)
{
if(0==vertexArr[idx].in)
zeroInputDegreeVertex.push(idx);
}
//初始化elv,每个顶点的最早开始时间,全部为0
for(int idx=0;idx!=vertexNum;++idx)
{
etv[idx] = 0;
}
//遍历输出拓扑排序
int cnt = 0;
cout<<"拓扑排序:";
while(!zeroInputDegreeVertex.empty())
{
int idx = zeroInputDegreeVertex.top();
cout<<vertexArr[idx].data<<" "; //输出一个度为0的顶点
zeroInputDegreeVertex.pop();
AOV.push(idx); //保存结果
++cnt;
for(edgeNode* node = vertexArr[idx].firstEdge;nullptr!=node;node=node->next)
{
if( (etv[idx]+node->weight) >
etv[node->vertexIdx] )
{ //从第一个入度为0的点,也就是源点,就可以开始计算从源点出发可以到达的点的elv了
etv[node->vertexIdx] = etv[idx]+node->weight;
}
--(vertexArr[node->vertexIdx].in);
if(0==vertexArr[node->vertexIdx].in)
zeroInputDegreeVertex.push(node->vertexIdx);
//这里比较特殊,node->vertexIdx == vertexArr[node->vertexIdx].data
}
}
cout<<endl;
if(cnt==vertexNum)
return 0;
else
return -1;
}
void CriticalPath_AOE(const meihao::Graph& g)
{
int vertexNum = g.getGraphVertexNumber();
stack<int> AOV;
int* etv = new int[vertexNum]; //顶点表示的事件的最早开始时间数组
vertexNode* vertexArr = nullptr;
int ret = TopologicalSort_AOV(g,vertexArr,etv,AOV);
if(-1==ret)
{
cout<<"TopologicalSort_AOV error!"<<endl;
return ;
}
//根据得到的AOE网的拓扑排序的逆序求ltv, 顶点表示的事件的最晚开始时间数组
int* ltv = new int[vertexNum];
for(int idx=0;idx!=vertexNum;++idx)
{
ltv[idx] = etv[vertexNum-1];
}
while(!AOV.empty())
{
int vertexIdx = AOV.top();
for(edgeNode* node = vertexArr[vertexIdx].firstEdge;nullptr!=node;node=node->next)
{
if( ltv[vertexIdx] >
ltv[node->vertexIdx]-node->weight )
ltv[vertexIdx] = ltv[node->vertexIdx]-node->weight;
}
AOV.pop();
}
/* test etv 和 ltv */
cout<<"etv"<<" ";
for(int idx=0;idx!=vertexNum;++idx)
{
cout<<etv[idx]<<" ";
}
cout<<endl;
cout<<"ltv"<<" ";
for(int idx=0;idx!=vertexNum;++idx)
{
cout<<ltv[idx]<<" ";
}
cout<<endl;
//现在已经得到了etv和ltv了,可以遍历图中所有顶点,根据etv和ltv求出对应顶点之间的活动的ete和lte
cout<<"关键路径:";
for(int idx=0;idx!=vertexNum;++idx)
{//从0号顶点开始,判断0号可以到达其他顶点之间的活动
for(edgeNode* node=vertexArr[idx].firstEdge;nullptr!=node;node=node->next)
{
//ete 活动最早开始时间,<idx,node,vertexIdx>, ete = etv[idx]
int ete = etv[idx];
//lte 活动最晚开始时间,<idx,node,vertexIdx>, lte = ltv[node->vertexIdx] - <idx,node,vertexIdx>
int lte = ltv[node->vertexIdx] - node->weight;
if(ete==lte) //相等,关键路径,关键活动
{
cout<<"<"<<idx<<","<<node->vertexIdx<<">="<<g.getGraphEdgeWeight(idx,node->vertexIdx)<<" ";
}
}
}
cout<<endl;
}
};
//最后记得释放动态开辟的内存空间
|
图的关键路径,AOE,完整实现,C++描述的更多相关文章
- c/c++求解图的关键路径 critical path
c/c++求解图的关键路径 critical path 上图表示一个工程,工程以V1为起始子工程,V9为终止子工程. 由图可以看出,要开工V5工程,必须在完成工程V2和V3后才可以. 完成V2需要a1 ...
- AOV图与拓扑排序&AOE图与关键路径
AOV网:所有的工程或者某种流程可以分为若干个小的工程或阶段,这些小的工程或阶段就称为活动.若以图中的顶点来表示活动,有向边表示活动之间的优先关系,则这样活动在顶点上的有向图称为AOV网. 拓扑排序算 ...
- 图->有向无环图->求关键路径
文字描述 与AOV-网相对应的是AOE-网(Activity on Edge)即边表示活动的网.AOE-网是一个带权的有向无环图.其中,顶点表示事件Event,弧表示活动,权表示活动持续的时间.通常, ...
- 图的邻接矩阵存储实现,C++描述
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 微信小程序多图上传/朋友圈传图效果【附完整源码】
效果图 部分源代码 js文件: var uploadPicture = require('../Frameworks/common.js') //获取应用实例 const app = getApp() ...
- AOE网与关键路径简介
前面我们说过的拓扑排序主要是为解决一个工程能否顺序进行的问题,但有时我们还需要解决工程完成需要的最短时间问题.如果我们要对一个流程图获得最短时间,就必须要分析它们的拓扑关系,并且找到当中最关键的流程, ...
- Linux下用火焰图进行性能分析【转】
转自:https://blog.csdn.net/gatieme/article/details/78885908 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...
- 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...
- 特征描述之LBP
LBP 在数字图像处理和模式识别领域,LBP指局部二值模式,英文全称:Local Binary Patterns.最初功能为辅助图像局部对比度,并不是一个完整的特征描述子. 后来提升为一种有效的纹理描 ...
随机推荐
- 第 8 章 容器网络 - 058 - flannel 概述
flannel 概述 flannel 是 CoreOS 开发的容器网络解决方案. flannel 为每个 host 分配一个 subnet,容器从此 subnet 中分配 IP,这些 IP 可以在 h ...
- php oracle数据库clob和nclob字段
php oracle数据库clob和nclob字段 nclob类型 1.nclob不能使用php的stream_get_contents来获取数据库的资源内容, 2.并且nclob只能使用to_cha ...
- LeetCode--367--有效的完全平方数
问题描述: 给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False. 说明:不要使用任何内置的库函数,如 sqrt. 示例 1: 输入:16 输 ...
- Linux系统起源及主流发行版
Linux系统起源及主流发行版 本文首先介绍了三大服务器系统,然后介绍了Linux系统的出现背景.以及主要release版本,最后介绍了Linux的文件系统和目录结构. 服务器系统,即安装在服 ...
- layui 表格图片放大
1. 表格塞图片 ,{title: '图片', width:120, templet: function(d) { return '<div onclick="show_img(thi ...
- 『TensotFlow』RNN中文文本_上
中文文字预处理流程 文本处理 读取+去除特殊符号 按照字段长度排序 辅助数据结构生成 生成 {字符:出现次数} 字典 生成按出现次数排序好的字符list 生成 {字符:序号} 字典 生成序号list ...
- javascript作用域、闭包、对象与原型链
原文作者总结得特别好,自己收藏一下.^-^ 1.作用域1.1函数作用域JS的在函数中定义的局部变量只对这个函数内部可见,称之谓函数作用域.它没有块级作用域(因此if.for等语句中的花括号不是独立作用 ...
- HDU-2586-裸LCA入门-tarjan离线
http://acm.hdu.edu.cn/showproblem.php?pid=2586 给出一颗树和边权,询问两点距离. 考虑tarjan离线做法,做法很巧妙,当前进行到u,对他的儿子v,当v子 ...
- UI基础五:简单的OP组件POPUP搜索帮助
需求:给一个配置表,需要根据配置表来弹出选择框,并将选择的数据添加到SALES ORDER的项目 BSP_WD_CMPWB 新建组件:ZHSI_JPMPG 新建视图,适用VALUE NODE 参考表Z ...
- CRM 价格批导
日了,好多代码....COPY别人的,懒得改了 *----------------------------------------------------------------------* *** ...