数据结构-考研难点代码突破 (图关键路径完全解析(流程+代码) - C++代码)

news2024/11/27 22:21:42

考研在关键路径上的考察以流程为主

文章目录

  • 1. AOE网
  • 2. 关键路径问题解决流程
    • C++代码

1. AOE网

首先区分AOV网:
AOV网∶若用DAG 图(有向无环图)表示一个工程,其顶点表示活动,用有向边<Vi,Vj>表示活动 Vi必须先于活动Vj进行的这样一种关系,则将这种有向图称为顶点表示活动的网络,记为 AOV网。

AOE网:
在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为用边表示活动的网络,简称AOE网

AOE网具有以下两个性质:

  1. 只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始
  2. 只有在进入某顶点的各有向边所代表的活动都已结束时,该顶点所代表的事件才能发生。(有些事件可以并行执行)

在AOE网中仅有一个入度为0的顶点,称为开始顶点(源点),它表示整个工程的开始;
也仅有一个出度为0的顶点,称为结束顶点(汇点),它表示整个工程的结束。

eg:
在这里插入图片描述
我们用有向无环网描述某项工程时,数据结构中称这样的网结构为AOE(Activity OnEdge)网,网中的顶点称为“事件”,弧称为“活动”,弧对应的权值代表活动持续的时间

关键路径:
从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动

完成整个工程的最短时间就是关键路径的长度,若关键活动不能按时完成,则整个工程的完成时间就会延长

2. 关键路径问题解决流程

为了解决关键路径问题,需要引入一下参数:

  1. 事件v的最早发生时间ve:决定了所有从v开始的活动能够开工的最早时间

  2. 活动a的最早开始时间e:指该活动弧的起点所表示的时间最早发生的时间

  3. 事件v的最迟发生时间vl:它是指在不推迟整个工程完成的前提下,该事件最迟必须发生的时间。

  4. 活动a最迟开始时间l:活动弧的重点表示的事件最迟发生时间vl-该活动所需要的时间差。

  5. 活动的时间余量:活动最迟开始时间-最早开始时间,表示在不增加完成整个工程所需总时间的情况下,活动a可以拖延的时间

    如果活动的时间余量为0,称为关键活动。

求关键活动步骤:

  1. 求所有事件的最早发生时间ve():

    按拓扑排序序列,依次求各个顶点的ve(k):ve(源点)=0
    ve(k)= Max {ve(j)+Weight(v,vk)},v为v的任意前驱
    在这里插入图片描述
    先求拓扑排序序列,按照拓扑排序序列计算事件最早发生时间ve(k)
    拓扑排序:1,2,3,4,5,6,7,8,9

    ve1=0,ve2=6,ve3=4,ve4=5,ve5=max(7,5)=7,ve6=7,ve7=16,ve8=14,v9=18

  2. 求所有事件的最迟发生时间vl()

    按逆拓扑排序序列,依次求各个顶点的vl(k):vl(汇点)= ve(汇点)
    vl(k)= Min{vl(j) - Weight(vi,vj)}, v为v的任意后继

    汇点最迟发生时间和最早发生时间相同

    在这里插入图片描述
    逆拓扑序列:9,8,7,6,5,4,3,2,1

    vl9=ve9=18,vl8=14,vl7=16,vl6=10,vl5=min(7,7)=7,vl4=8,vl3=6,vl2=6,vl1=min(0,2,3)=0

  3. 求所有活动的最早发生时间e()

    若边<vk,vj>表示活动ai,则有e(i)= ve(k)

    在这里插入图片描述
    e1=0,e2=0,e3=0,e4=6,e5=4,e6=5,e7=7,e8=7,e9=7,e10=16,e11=14

  4. 求所有活动的最迟发生时间I():

    若边<vk,vj>表示活动ai,则有l(i)= vl(j) - Weight(vk, v))

在这里插入图片描述
l11=14,l10=16,l9=10,l8=7,l7=7,l6=8,l5=6,l4=6,l3=3,l2=2,l1=0

  1. 求所有活动的时间余量d(),d(i)=0的活动就是关键活动,由关键活动可得关键路径

    最晚活动时间-最早活动时间d(i)= l(i)-e(i)

在这里插入图片描述
d1=0,d2=2-0,d3=3-0,d4=6-6=0,d5=6-4,d6=8-5,d7=7-7=0d8=7-7=0,d9=10-7,d10=10-10=0,d11=14-14=0

根据上图分析,关键活动为:a1,a4,a7,a8,a10,a11

关键路径为:
<V1,V2> <V2,V5> <V5,V8> <V5,V7> <V7,V9> <V8,V9>

需要注意:

  1. 若关键活动耗时增加则整个工程的工期将增长
  2. 缩短关键活动的时间,可以缩短整个工程的工期
  3. 当缩短到一定程度时,关键活动可能会变成非关键活动
  4. 可能有多条关键路径,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的。(上图表示为加速a1,a4执行速度来缩短工程时间)

C++代码

#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 20 // 最大顶点个数
#define VertexType int    // 顶点数据的类型

// 建立全局变量,存储各个顶点(事件)的最早发生时间
VertexType ve[MAX_VERTEX_NUM] = {0};
// 建立全局变量,保存各个顶点(事件)的最晚发生时间
VertexType vl[MAX_VERTEX_NUM] = {0};

typedef struct ArcNode
{
    int adjvex;              // 邻接点在数组中的位置下标
    struct ArcNode *nextarc; // 指向下一个邻接点的指针
    VertexType dut;
} ArcNode;

typedef struct VNode
{
    VertexType data;              // 顶点的数据域
    ArcNode *firstarc;            // 指向邻接点的指针
} VNode, AdjList[MAX_VERTEX_NUM]; // 存储各链表头结点的数组

typedef struct
{
    AdjList vertices;   // 图中顶点及各邻接点数组
    int vexnum, arcnum; // 记录图中顶点数和弧数
} ALGraph;

// 找到顶点在邻接表数组中的位置下标
int LocateVex(ALGraph G, VertexType u)
{
    int i;
    for (i = 0; i < G.vexnum; i++)
    {
        if (G.vertices[i].data == u)
        {
            return i;
        }
    }
    return -1;
}

// 邻接表存储 AOE 网(有向无环网)
void CreateAOE(ALGraph *G)
{
    int i, locate;
    VertexType initial, end, dut;
    ArcNode *p = NULL;
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    for (i = 0; i < G->vexnum; i++)
    {
        scanf("%d", &(G->vertices[i].data));
        G->vertices[i].firstarc = NULL;
    }
    for (i = 0; i < G->arcnum; i++)
    {
        scanf("%d,%d,%d", &initial, &end, &dut);
        p = (ArcNode *)malloc(sizeof(ArcNode));
        p->adjvex = LocateVex(*G, end);
        p->nextarc = NULL;
        p->dut = dut;
        locate = LocateVex(*G, initial);
        p->nextarc = G->vertices[locate].firstarc;
        G->vertices[locate].firstarc = p;
    }
}

// 结构体定义栈结构
typedef struct stack
{
    VertexType data;
    struct stack *next;
} stack;

// 初始化栈结构
void initStack(stack **S)
{
    (*S) = (stack *)malloc(sizeof(stack));
    (*S)->next = NULL;
}

// 判断栈是否为空
bool StackEmpty(stack S)
{
    if (S.next == NULL)
    {
        return true;
    }
    return false;
}

// 进栈,以头插法将新结点插入到链表中
void push(stack *S, VertexType u)
{
    stack *p = (stack *)malloc(sizeof(stack));
    p->data = u;
    p->next = NULL;
    p->next = S->next;
    S->next = p;
}

// 弹栈函数,删除链表首元结点的同时,释放该空间,并将该结点中的数据域通过地址传值给变量i;
void pop(stack *S, VertexType *i)
{
    stack *p = S->next;
    *i = p->data;
    S->next = S->next->next;
    free(p);
}

void deleStack(stack *S)
{
    stack *del = NULL;
    if (S->next)
    {
        del = S->next;
        S->next = del->next;
        free(del);
    }
    free(S);
}

// 创建一个全局指针,后续会指向一个链栈
stack *T = NULL;

// 统计各顶点的入度
void FindInDegree(ALGraph G, int indegree[])
{
    int i;
    // 遍历邻接表,根据各链表中数据域存储的各顶点位置下标,在indegree数组相应位置+1
    for (i = 0; i < G.vexnum; i++)
    {
        ArcNode *p = G.vertices[i].firstarc;
        while (p)
        {
            indegree[p->adjvex]++;
            p = p->nextarc;
        }
    }
}

bool TopologicalOrder(ALGraph G)
{
    int index, i, indegree[MAX_VERTEX_NUM] = {0}; // 创建记录各顶点入度的数组
    // 建立链栈
    stack *S = NULL;
    int count = 0;
    ArcNode *p = NULL;
    FindInDegree(G, indegree); // 统计各顶点的入度
    // 初始化栈
    initStack(&S);
    // 查找度为0的顶点,作为起始点
    for (i = 0; i < G.vexnum; i++)
    {
        if (!indegree[i])
        {
            push(S, i);
        }
    }
    // 栈为空为结束标志
    while (!StackEmpty(*S))
    {
        // 弹栈,并记录栈中保存的顶点所在邻接表数组中的位置
        pop(S, &index);
        // 压栈,为求各边的最晚开始时间做准备(实现拓扑排序的逆过程)
        push(T, index);
        ++count;
        // 依次查找跟该顶点相关联的顶点,将它们的入度减1,然后将入度为 0 的顶点入栈
        for (p = G.vertices[index].firstarc; p; p = p->nextarc)
        {
            VertexType k = p->adjvex;
            if (!(--indegree[k]))
            {
                // 顶点入度为0,入栈
                push(S, k);
            }
            // 如果 index 的最早发生时间加上边的权值比 k 的最早发生时间还长,就覆盖ve数组中对应位置的值,最终结束时,ve数组中存储的就是各顶点的最早发生时间。
            if (ve[index] + p->dut > ve[k])
            {
                ve[k] = ve[index] + p->dut;
            }
        }
    }
    deleStack(S);
    // 如果count值小于顶点数量,表明有向图有环
    if (count < G.vexnum)
    {
        printf("该图有回路");
        return false;
    }
    return true;
}

// 求各顶点的最晚发生时间并计算出各边的最早和最晚开始时间
void CriticalPath(ALGraph G)
{
    int i, j, k;
    ArcNode *p = NULL;
    if (!TopologicalOrder(G))
    {
        return;
    }
    for (i = 0; i < G.vexnum; i++)
    {
        vl[i] = ve[G.vexnum - 1];
    }
    // 计算各个顶点的最晚发生时间
    while (!StackEmpty(*T))
    {
        pop(T, &j);
        for (p = G.vertices[j].firstarc; p; p = p->nextarc)
        {
            k = p->adjvex;
            // 构建Vl数组,在初始化时,Vl数组中每个单元都是18,如果每个边的汇点-边的权值比源点值小,就保存更小的。
            if (vl[k] - p->dut < vl[j])
            {
                vl[j] = vl[k] - p->dut;
            }
        }
    }
    printf("关键路径为\n");
    // 结合 ve 和 vl 数组中存储的数据,可以计算出每条边的最早开始时间和最晚开始时间,进而判断各个边是否为关键路径
    for (j = 0; j < G.vexnum; j++)
    {
        int ee, el;
        for (p = G.vertices[j].firstarc; p; p = p->nextarc)
        {
            k = p->adjvex;
            // 求各边的最早开始时间e[i],等于ve数组中相应源点存储的值
            ee = ve[j];
            // 求各边的最晚开始时间l[i],等于汇点在vl数组中存储的值减改边的权值
            el = vl[k] - p->dut;
            // 判断e[i]和l[i]是否相等,如果相等,该边就是关键活动,相应的用*标记;反之,边后边没标记
            if (ee == el)
            {
                printf("<V%d,V%d> ", j + 1, k + 1);
            }
        }
    }
}

#include "criticalpath.h"

int main()
{
    ALGraph G;
    CreateAOE(&G); // 创建AOE网
    initStack(&T);
    TopologicalOrder(G);
    CriticalPath(G);
    deleStack(T);
    system("pause");
    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ESP32+freeRTOS学习笔记-(五)队列Queue】

目录1、什么是队列Queue2、队列的多任务特性2.1 多任务的访问&#xff1a;2.2 队列读取阻塞&#xff1a;2.3 写队列阻塞&#xff1a;2.4 阻塞于多个队列&#xff1a;3、队列的使用3.1 创建队列--The xQueueCreate() API3.2 写入队列3.3 从队列中接收数据3.4 删除队列4、队列集4…

ReactDOM.render在react源码中执行之后发生了什么?

ReactDOM.render 通常是如下图使用&#xff0c;在提供的 container 里渲染一个 React 元素&#xff0c;并返回对该组件的引用&#xff08;或者针对无状态组件返回 null&#xff09;。本文主要是将ReactDOM.render的执行流程在后续文章中会对创建更新的细节进行分析&#xff0c…

MATLAB-plot3/ezplot3三维绘图

&#xff08;1&#xff09; plot3是三维绘图的基本函数&#xff0c;调用格式如下。1、plot3( X,Y,Z):绘制简单的三维曲线&#xff0c;当X、Y、Z是长度相同的向量时&#xff0c;plot3命令将绘制以向量X、Y、Z为(x, y,z)坐标值的三维曲线;当X、Y、Z是mn矩阵时,plot3命令将绘制m条…

Android 虚拟分区详解(四) 编译开关

Android Virtual A/B 系统简称 VAB,我将其称为虚拟分区。 本系列文章基于 Android R(11) 进行分析,如果没有特别说明,均基于代码版本 android-11.0.0_r46 请已经购买《Android 虚拟分区》专栏的朋友加我 wx 进 "虚拟分区专栏 VIP 答疑"群,作为本专栏文章的附加服…

(6)元对象系统与信号与槽机制

1. 元对象系统 元对象系统是一个基于标准C的扩展&#xff0c;为Qt提供了信号与槽机制、实时类型信息、动态属性系统。 什么是元对象 在计算机科学中&#xff0c;元对象是这样一个东西&#xff1a;它可以操纵、创建、描述、或执行其他对象。元对象描述的对象称为基对象。元对象可…

记一次搭建备库,使用连接串主库无法连接到备库

主库使用连接串连接备库失败 SQL> conn sys/oracleorcldg as sysdba ERROR: ORA-12528: TNS:listener: all appropriate instances are blocking new connections 备库已经建立了静态监听 # listener.ora Network Configuration File: /u01/app/oracle/product/11.2.0/db_1/…

安全寒假第一堂课

一、状态码 200 – 服务器成功返回网页 404 – 请求的网页不存在 503 – 服务器超时 1xx&#xff08;临时响应&#xff09; 表示临时响应并需要请求者继续执行操作的状态码。 100&#xff08;继续&#xff09; 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一…

OpenCV实战(5)——图像运算详解

OpenCV实战&#xff08;5&#xff09;——图像运算详解0. 前言1. 图像基本运算2. 重载图像运算符2.1 加法运算符重载2.2 分割图像通道2.3 完整代码3. 图像重映射3.1 OpenCV 重映射函数3.2 完整代码小结系列链接0. 前言 图像可以以不同的方式进行组合&#xff0c;因为它们是正则…

XGBoost论文阅读

XGBoost: A Scalable Tree Boosting System 目录 XGBoost: A Scalable Tree Boosting System 1.摘要 2.方法 2.1 正则化学习目标 2.2 梯度提升树 2.3 收缩率和列采样 2.4分裂点查找算法 1.摘要 提出了一种新的稀疏性感知算法&#xff0c;用于稀疏数据和加权全图草图&a…

Python教程:什么是三级模式和二级映像?

美国国家标准学会(American National Standards Institute,ANSI)所属的标准计划与需求委员会&#xff08;Standards Planning and Requirements Committee,SPARC)在1971年公布的研究报告中提出了ANSI-SPARC体系结构&#xff0c;即三级模式结构&#xff08;或称为三层体系结构&a…

ArcGIS基础实验操作100例--实验53导出线、面要素的坐标值

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验53 导出线、面要素的坐标值 目录 一、实验背景 二、实验数据 三、实验步骤 &#xf…

笔记杂项(一)

都是踩过的坑&#xff0c;趟过的水。 ubuntu虚拟机终端字体太小的设置方法&#xff1a;ubuntu18.04调整终端字体大小 这个方法试试看&#xff1a;https://zhuanlan.zhihu.com/p/139305626 容器里面编译内核代码&#xff0c;进程被杀掉的原因是触发了内核OOM killer&#xff0c…

干货| app自动化测试之Andriod微信小程序的自动化测试

随着微信小程序的功能和生态日益完善&#xff0c;很多公司的小程序项目页面结构越来越多&#xff0c;业务逻辑也越来越复杂。如何做好小程序的自动化测试就成为测试同学普遍面临的一大痛点难题。微信小程序小程序内嵌于微信内部&#xff0c;页面包含 Native 原生元素和 Web 元素…

华为防火墙与二层交换机对接配置VLAN上网设置

拓扑图 一、防火墙设置 1、G1/0/0接口设置IP&#xff0c;G1/0/1接口切换二层口设置VLAN&#xff0c;G1/0/0 桥接了本地无线网卡来模拟公网地址 <USG6000V1>sys [USG6000V1]sys FW1 [FW1]un in en# 设置公网IP [FW1]int g1/0/0 [FW1-GigabitEthernet1/0/0]ip addr 192.1…

package.json配置解读之入门

文章目录前言一、描述配置nameversionrepositorydescriptionkeywordslicenseauthor二、文件配置filestypemainbrowsermoduleexportsworkspaces三、脚本配置scriptsconfig四、结语前言 package.json是每个前端项目都会有的json文件&#xff0c;位于项目的根目录中。很多脚手架在…

RHCE(chrony服务器)

chrony服务器 chrony服务器是一个开源自由的网络时间协议NTP的客户端和服务器的软件&#xff0c;他能让计算机保持系统时钟和时钟服务器保持同步&#xff0c;让计算机保持精确的时间&#xff0c;chrony也可以作为服务端软件为其他计算机提供时间同步服务 chrony由两部分组成&…

openAI--十拳剑助你做AI时代的弄潮儿

AI它厉害&#xff08;diao&#xff09;吗&#xff1f; 最近大家玩chatgpt还好吗&#xff1f; 有被它的恋爱情商暴击到吗&#xff1f; 有没有觉得那在leetcode上所向无敌的技巧都是浮云吗&#xff1f; 今天&#xff0c;我为大家带来十个很好的AI平台。这一篇先介绍一下&…

【远程桌面】nomachine下载安装使用教程、zerotier下载安装使用教程超详细

文章目录一、软件介绍二、NoMachine远程桌面1.Windows下载安装使用2.Linux下载安装使用3.Android下载安装使用4.ARM下载安装使用&#xff08;未实践&#xff09;三、ZeroTier内网穿透0.官网注册账户1.Windows下载安装使用2.Linux下载安装使用3.Android下载安装使用4.ARM下载安装…

Android 学习 - 不完善

SharedPreference 共享参数用法 SharedPreference 是 Android 的一个轻量级存储工具, 采用的存储结构是Key-Value的键值对方式. 共享参数的存储介质是符合XML规范的配置文件. 保存路径是: /data/data/应用包名/shared_prefs/文件名.xml 利用元数据配置快捷菜单 (1)元数据的met…

【阶段二】Python数据分析Pandas工具使用11篇:探索性数据分析:数据的检验:卡方检验与t检验

本篇的思维导图: 探索性数据分析:数据的检验 卡方检验 在实际的学习或工作中,也会碰到关于离散型变量之间的探索性分析,如两个离散变量之间是否相互独立。对于该问题的解答,就需要运用统计学中的卡方检验了。卡方检验属于非参数的检验方法,其原假设是两个离散变…