算法:求解AOE网的关键路径

前面我们简要地介绍了AOE网和关键路径的一些概念,本文接着对求解关键路径程序的主要函数进行分析。现有一AOE网图如图7-9-4所示,我们使用邻接表存储结构,注意与拓扑排序时邻接表结构不同的地方在于,这里弧表结点增加了weight域,用来存储弧的权值。


求解事件的最早发生时间etv的过程,就是我们从头至尾找拓扑序列的过程,因此在求关键路径之前,需要先调用一次拓扑序列算法的代码来计算etv 和 拓扑序列列表,我们针对前面讲过的AOV网与拓扑排序的程序进行改进,代码如下(参考《大话数据结构》):

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* 拓扑排序,若GL无回路,则输出拓扑排序序列并返回1,若有回路返回0。 */
bool TopologicalSort(GraphAdjList GL)
{
    EdgeNode *pe;
     int i, k, gettop;
     int top =  0; /* 用于栈指针下标  */
     int count =  0; /* 用于统计输出顶点的个数  */
     /* 建栈将入度为0的顶点入栈  */
     int *stack = ( int *)malloc(GL->numVertexes *  sizeof( int));

     for (i =  0; i < GL->numVertexes; i++)
         if ( 0 == GL->adjList[i].in)
            stack[++top] = i; /* 将入度为0的顶点入栈 */

    top2 =  0;
    etv = ( int *)malloc(GL->numVertexes *  sizeof( int));
     for (i =  0; i < GL->numVertexes; i++)
        etv[i] =  0/* 初始化 */
    stack2 = ( int *)malloc(GL->numVertexes *  sizeof( int));

    cout <<  "TopologicalSort ..." << endl;

     while (top !=  0)
    {
        gettop = stack[top--];
        cout << GL->adjList[gettop].data <<  " -> ";
        count++;   /* 输出i号顶点,并计数 */

        stack2[++top2] = gettop;  /* 将弹出的顶点序号压入拓扑序列的栈 */

         for (pe = GL->adjList[gettop].firstedge; pe; pe = pe->next)
        {
            k = pe->adjvex;
             /* 将i号顶点的邻接点的入度减1,如果减1后为0,则入栈 */
             if (!(--GL->adjList[k].in))
                stack[++top] = k;
             /* 求各顶点事件的最早发生时间etv值 */
             if ((etv[gettop] + pe->weight) > etv[k])
                etv[k] = etv[gettop] + pe->weight;
        }
    }
    cout << endl;
     if (count < GL->numVertexes)
         return  false;
     else
         return  true;
}


在程序开始处我们声明了几个全局变量:

int *etv,*ltv; /* 事件最早发生时间和最迟发生时间数组,全局变量 */
int *stack2;   /* 用于存储拓扑序列的栈 */
int top2;   /* 用于stack2的指针 */

其中stack2用来存储拓扑序列,以便后面求关键路径时使用。

上面的拓扑排序函数中除了增加了第12~19行,29行,38~39行,其他跟前面讲过的AOV网与拓扑排序没什么区别。

第12~19行初始化全局变量etv数组、top2和stack2的过程。第29行就是将本来要输出的拓扑序列压入全局栈stack2中。第38~39行很关键,是求etv数组的每一个元素的值,具体求值办法参见AOE网和关键路径


下面来看求关键路径的算法代码。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* 求关键路径,GL为有向网,输出G的各项关键活动 */
void CriticalPath(GraphAdjList GL)
{
    EdgeNode *pe;
     int i, j, k, gettop;
     int ete, lte; /* 声明活动最早发生时间和最迟发生时间变量 */
    TopologicalSort(GL); /* 求拓扑序列,计算数组etv和stack2的值 */

    ltv = ( int *)malloc(GL->numVertexes *  sizeof( int));  /* 事件最早发生时间数组 */

     for (i =  0; i < GL->numVertexes; i++)
        ltv[i] = etv[GL->numVertexes -  1]; /* 初始化 */

    cout <<  "etv :  ";
     for (i =  0; i < GL->numVertexes; i++)
        cout << etv[i] <<  ' ';
    cout << endl;

     while (top2 !=  0) /* 出栈是求ltv */
    {
        gettop = stack2[top2--];
         /* 求各顶点事件的最迟发生时间ltv值 */
         for (pe = GL->adjList[gettop].firstedge; pe; pe = pe->next)
        {
            k = pe->adjvex;
             if (ltv[k] - pe->weight < ltv[gettop])
                ltv[gettop] = ltv[k] - pe->weight;
        }
    }

    cout <<  "ltv :  ";
     for (i =  0; i < GL->numVertexes; i++)
        cout << ltv[i] <<  ' ';
    cout << endl;
     /* 求ete,lte和关键活动 */
     for (j =  0; j < GL->numVertexes; j++)
    {
         for (pe = GL->adjList[j].firstedge; pe; pe = pe->next)
        {
            k = pe->adjvex;
            ete = etv[j]; /* 活动最早发生时间 */
            lte = ltv[k] - pe->weight; /* 活动最迟发生时间 */
             if (ete == lte)  /* 两者相等即在关键路径上 */
                cout <<  "<v" << GL->adjList[j].data <<  " - v"
                     << GL->adjList[k].data <<  "> length: " << pe->weight << endl;
        }
    }
}

函数第7行调用求拓扑序列的函数,执行完毕后,全局数组etv和栈stack2 如图7-9-6所示,top2 = 10,也就是说,对于每个事件的最早发生时间,我们已经计算出来了。


第11~12行初始化全局变量ltv数组,因为etv[9] = 27,所以数组ltv值现在为全27。

第19~29行是计算ltv 数组的循环,具体方法参见AOE网和关键路径

当程序执行到第36行,etv和ltv数组的值如图7-9-9


比如etv[1] = 3, 而ltv[1] = 7,表示如果单位是天的话,哪怕v1整个事件在第7天才开始,也可以保证整个工程的按期完成,可以提前v1事件开始时间,但最早也得第3天开始。

第36~47行是求另两个变量,活动最早开始时间ete和活动最晚开始时间lte,并对相同下标的它们进行比较。两重循环嵌套是对邻接表的顶点和每个顶点的弧表遍历,具体方法参见AOE网和关键路径,举例来说,如图7-9-10,当j = 0时,当k = 2, ete = lte, 表示

弧<v0, v2> 是关键路径,因此打印;当k = 1, ete != lte, 故弧<v0, v1> 不是关键路径。



j = 1 一直到 j = 9为止,做法是完全相同的,最后输出的结果如下图,最终关键路径如图7-9-11所示。




  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值