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;}

AOE网(Activity On Edge NetWword):
  在表示一个工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE网(Activity On Edge NetWword)。
  AOE网中没有入边的顶点称为始点(源点),没有出边的顶点称为终点(汇点)


AOV & AOE
  AOE网是顶点表示活动的网,只描述活动之间的制约关系;AOE网是用边表示活动的网,边上的权值表示活动持续的时间。
关键路径:开始——发动机完成——部件集中到位——组装完成 = 5.5
关键路径:
  我们把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动
//对应顶点表和边表结点
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>
下标 0 1 2 3 4 5 6 7 8 9
etv 0 3 4 8 12 9 15 11 24 19 17 24 21 27
下标 0 1 2 3 4 5 6 7 8 9
ltv 0 9 7 4 6 12 16 15 13 25 19 24 27

初始etv全部为0,ltv为tev[9]

下标 0 1 2 3 4 5 6 7 8 9 10 11 12
ete 0 0 3 3 4 4 12 15 15 11 24 19 24
下标 0 1 2 3 4 5 6 7 8 9 10 11 12
lte 4 0 7 9 4 6 12 16 15 13 25 19 24


由于ete和lte中我们的数组下标都是假定我们知道图的具体形状,边和顶点的关系给标号来,在算法过程中我们没法确定边是由哪两个顶点得到的,但是我们可以得出etv和ltv,最后根据etv和ete、ltv和lte的关系在一次遍历过程中根据关键活动ete=lte来立即输出边。

关键活动:ete = lte

a1 → a4 → a6 → a8 → a11 → a12
<V0,V2>4 → <V2,V3>8 → <V3,V4>3 → <V4,V7>4 → <V7,V8>5 → <V8,V9>3


算法要首先确定有向图是不是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++描述的更多相关文章

  1. c/c++求解图的关键路径 critical path

    c/c++求解图的关键路径 critical path 上图表示一个工程,工程以V1为起始子工程,V9为终止子工程. 由图可以看出,要开工V5工程,必须在完成工程V2和V3后才可以. 完成V2需要a1 ...

  2. AOV图与拓扑排序&AOE图与关键路径

    AOV网:所有的工程或者某种流程可以分为若干个小的工程或阶段,这些小的工程或阶段就称为活动.若以图中的顶点来表示活动,有向边表示活动之间的优先关系,则这样活动在顶点上的有向图称为AOV网. 拓扑排序算 ...

  3. 图->有向无环图->求关键路径

    文字描述 与AOV-网相对应的是AOE-网(Activity on Edge)即边表示活动的网.AOE-网是一个带权的有向无环图.其中,顶点表示事件Event,弧表示活动,权表示活动持续的时间.通常, ...

  4. 图的邻接矩阵存储实现,C++描述

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  5. 微信小程序多图上传/朋友圈传图效果【附完整源码】

    效果图 部分源代码 js文件: var uploadPicture = require('../Frameworks/common.js') //获取应用实例 const app = getApp() ...

  6. AOE网与关键路径简介

    前面我们说过的拓扑排序主要是为解决一个工程能否顺序进行的问题,但有时我们还需要解决工程完成需要的最短时间问题.如果我们要对一个流程图获得最短时间,就必须要分析它们的拓扑关系,并且找到当中最关键的流程, ...

  7. Linux下用火焰图进行性能分析【转】

    转自:https://blog.csdn.net/gatieme/article/details/78885908 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

  8. 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车

     阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...

  9. 特征描述之LBP

    LBP 在数字图像处理和模式识别领域,LBP指局部二值模式,英文全称:Local Binary Patterns.最初功能为辅助图像局部对比度,并不是一个完整的特征描述子. 后来提升为一种有效的纹理描 ...

随机推荐

  1. C# 递归缩小图片

    需求:图片太大,上传到服务器会非常占用服务器空间,而系统又不要求高清图片,于是就通过递归的方式让图片每次减少10%的大小,当图片大小小于100k的时候就保存在本地,核心代码如下: class Prog ...

  2. Axure 第一次交互 实现跳转页面

  3. servlet容器、IOC容器、SpirngMVC

    servlet容器(这里指tomcat插件)存放servlet对象,而SpringMVC框架整个是一个servlet对象,而IOC容器 在Boot框架中,会存放产生servlet容器的工厂,工厂依据主 ...

  4. Getting Started with Processing 第五章的easing问题

    分析 使用 easing easing 的感官目的是为了 draw 的时候,画的图形不是即时 mouseX 的值,而是稍有落后一点.从算法分析,就是让所画图形的 x 坐标 落后于 mouseX 的值, ...

  5. 雷林鹏分享:jQuery EasyUI 树形菜单 - 树形菜单拖放控制

    jQuery EasyUI 树形菜单 - 树形菜单拖放控制 当在一个应用中使用树(Tree)插件,拖拽(drag)和放置(drop)功能要求允许用户改变节点位置.启用拖拽(drag)和放置(drop) ...

  6. 纯js无缝滚动

    HTML代码 <!--父容器要使用overflow: hidden;--> <div id="imgsList" style="height:150px ...

  7. vue.js如何实现点击按钮动态添加li

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. thinkphp5.0写的项目放到服务器上 lnmp 404

    tp5在Nginx上不适用pathinfo格式的url,在项目的Nginx配置文件里找到include enable-php.conf 改为 include enable-php-pathinfo.c ...

  9. matlab:统计矩阵中某元素的个数

    三种统计方法: A=ceil(rand(,)*); a=; %第一种 sum(A(:)==a): %第二种 length(find(A==a); %第三种 logical=(A=a); sum(log ...

  10. python FileNotFoundError: [WinError 2] 系统找不到指定的文件。

    D:\Test\TestSWP\Scripts\python.exe D:/Test/SWP/TestSWP/chorme/chorme.pyTraceback (most recent call l ...