天天看點

【筆記】AOE網與關鍵路徑1.AOE網2.關鍵路徑3.求關鍵路徑的算法實作

  • AOE網
  • 關鍵路徑
  • 求關鍵路徑的算法實作

  AOE網是以邊表示活動的有向無環網,在AOE網中,具有最大路徑長度的路徑稱為關鍵路徑,關鍵路徑表示完成工程的最短工期。

1.AOE網

  AOE網是一個帶權的有向無環圖。其中用頂點表示事件,弧表示活動,權值表示兩個活動持續的時間。AOE網是以邊表示活動的網。

  AOV網描述了活動之間的優先關系,可以認為是一個定性的研究,但是有時還需要定量地研究工程的進度,如整個工程的最短完成時間、各個子工程影響整個工程的程度、每個子工程的最短完成時間和最長完成時間。在AOE網中,通過研究事件和活動之間的關系,可以确定整個工程的最短完成時間,明确活動之間的互相影響,確定整個工程的順利進行。

  在用AOE網表示一個工程計劃時,用頂點表示各個事件,弧表示子工程的活動,權值表示子工程的活動需要的時間。在頂點表示事件發生之後,從該頂點出發的有向弧所表示的活動才能開始。在進入某個頂點的有向弧所表示的活動完成之後,該頂點表示的事件才能發生。

  對一個工程來說,隻有一個開始狀态和一個結束狀态。是以在AOE網中,隻有一個入度為零的點表示工程的開始,稱為源點;隻有一個出度為零的點表示工程的結束,稱為彙點。

【筆記】AOE網與關鍵路徑1.AOE網2.關鍵路徑3.求關鍵路徑的算法實作

2.關鍵路徑

  關鍵路徑是指在AOE網中從源點到彙點路徑最長的路徑。這裡的路徑長度是指路徑上各個活動持續時間之和。在AOE網中,有些活動是可以并行執行的,關鍵路徑其實就是完成工程的最短時間所經過的路徑。關鍵路徑上的活動稱為關鍵活動。

  1. 事件 vi 的最早發生時間:從源點到頂點 vi 的最長路徑長度,稱為事件 vi 的最早發生時間,記作ve(i)。求解ve(i)可以從源點ve(0)=0開始,按照拓撲排序規則根據遞推得到:

ve(i)=Max{ve(k)+dut(<k,j>∈)|<k,j>T,1≤i≤n−1}

  其中T是所有以第i個頂點為弧頭的弧的集合, dut(<k,j>) 表示弧 <k,j> <script type="math/tex" id="MathJax-Element-7577"> </script>對應的活動的持續時間。

  2. 事件 vi 的最晚發生時間:在保證整個工程完成的前提下,活動最遲的開始時間,記作vl(i)。z 求解 vi 的最早發生時間ve(i)的前提vl(n-1)=ve(n-1)下,從彙點開始,向源點推進得到:

vl(i)=Min{vl(k)−dut(<i,k>)|<i,k>∈S,0≤i≤n−2}

  其中S是所有以第i個頂點為弧尾的弧的集合, dut(<i,k>) 表示弧 <i,k> <script type="math/tex" id="MathJax-Element-7582"> </script>對應的活動的持續時間。

  3.活動 ai 的最早開始時間e(i):如果弧 <vk,vj> <script type="math/tex" id="MathJax-Element-7584"> </script>表示活動 ai 才開始。是以事件 vk 的最早發生時間也就是活動 ai 的最早開始時間,即e(i)=ve(k)。

  4.活動 ai 的最晚開始時間l(i):在不推遲整個工程完成時間的基礎上,活動 ai 最遲必須開始的事件。如果弧 <vk,vj> <script type="math/tex" id="MathJax-Element-7590"> </script>表示活動 ai ,持續時間為 dut(<k,j>) ,則活動 ai 的最晚開始時間 l(i)=vl(j)−dut(<k,j>) 。

  5.活動 ai 的松弛時間:活動 ai 的最晚開始時間域最早開始時間之差就是活動 ai 的松弛時間,記作l(i)-e(i)。

  當e(i)=l(i)時,對應的活動 ai 稱為關鍵活動,非關鍵活動提前完成或推遲完成并不會影響到整個工程的進度。

  求AOE網的關鍵路徑的算法:

  1. 對AOE網中的頂點進行拓撲排序,如果得到的拓撲序列頂點個數小于網中頂點數,則說明網中有環存在,不能求關鍵路徑,終止算法。否則,從源點 v0 開始,求出各個頂點的最早發生時間ve(i)。

  2. 從彙點 vn 出發vl(n-1)=ve(n-1),按照逆拓撲序列求其他頂點的最晚發生時間vl(i)。

  3. 由各頂點的最早發生時間ve(i)和最晚發生時間vl(i),求出每個活動 ai 的最早開始時間e(i)和最晚開始時間l(i)。

  4. 找出所有滿足條件e(i)=l(i)的活動 ai , ai 即是關鍵活動。

【筆記】AOE網與關鍵路徑1.AOE網2.關鍵路徑3.求關鍵路徑的算法實作

  關鍵路徑經過的頂點是滿足條件ve(i)==vl(i),即當事件的最早發生時間與最晚發生時間相等時,該頂點一定在關鍵路徑之上。同樣,關鍵活動者的弧滿足條件e(i)=l(i),即當活動的最早開始時間域最晚開始時間相等時,該活動一定是關鍵活動。是以,要求關鍵路徑,需要首先求出網中每個頂點的對應事件的最早開始時間,然後推出事件的最晚開始時間和活動的最早、最晚開始時間,最後再判斷頂點是否在關鍵路徑之上,得到網的關鍵路徑。

  要求每一個頂點的最早開始時間,首先要将網中的頂點進行拓撲排序。在對頂點進行拓撲排序的過程中,同時計算頂點的最早發生時間ve(i)。從源點開始,由與源點相關聯的弧的權值,可以得到該弧相關聯頂點對應事件的最早發生時間。同時定義一個棧T,儲存頂點的逆拓撲序列。

3.求關鍵路徑的算法實作

  采用鄰接表建立上圖所示的有向網,并求網中頂點的拓撲序列,然後計算該有向網的關鍵路徑。

  • 頭檔案棧
#define StackSize 100
typedef struct
{
    DataType stack[StackSize];
    int top;
}SeqStack;

void InitStack(SeqStack *S)    
/*将棧初始化為空棧隻需要把棧頂指針top置為0*/
{
S->top=;   /*把棧頂指針置為0*/
}
int StackEmpty(SeqStack S)   
/*判斷棧是否為空,棧為空傳回1,否則傳回0*/
{
    if(S.top==)            /*判斷棧頂指針top是否為0*/
        return ;           /*當棧為空時,傳回1;否則傳回0*/
    else
        return ;
}
int GetTop(SeqStack S, DataType *e)   
/*取棧頂元素。将棧頂元素值傳回給e,并傳回1表示成功;否則傳回0表示失敗。*/
{
   if(S.top<=)     /*在取棧頂元素之前,判斷棧是否為空*/
{
    printf("棧已經空!\n");
    return ;
}
else
{
    *e=S.stack[S.top-];    /*在取棧頂元素*/
    return ;
}
}
int PushStack(SeqStack *S,DataType e)   
/*将元素e進棧,元素進棧成功傳回1,否則傳回0.*/
{
if(S->top>=StackSize)               /*在元素進棧前,判斷是否棧已經滿*/
{
        printf("棧已滿,不能進棧!\n");
        return ;
}
else
{
        S->stack[S->top]=e;         /*元素e進棧*/
        S->top++;                   /*修改棧頂指針*/
        return ;
}
}
int PopStack(SeqStack *S,DataType *e)
/*出棧操作。将棧頂元素出棧,并将其指派給e。出棧成功傳回1,否則傳回0*/
{
    if(S->top<=)       /*元素出棧之前,判斷棧是否為空*/
    {
        printf("棧已經沒有元素,不能出棧!\n");
        return ;
    }
    else
{
    S->top--;           /*先修改棧頂指針,即出棧*/
        *e=S->stack[S->top];    /*将出棧元素指派給e*/
        return ;
    }
}
int StackLength(SeqStack S)
/*求棧的長度,即棧中元素個數,棧頂指針的值就等于棧中元素的個數*/
{
    return S.top;
}
void ClearStack(SeqStack *S)    
/*将棧初始化為空棧隻需要把棧頂指針top置為0*/
{
S->top=;   /*把棧頂指針置為0*/
}
           
  • 類型定義
#include<stdlib.h>
#include<stdio.h>
#include<malloc.h>
#include<string.h>
typedef int DataType;           /*棧元素類型定義*/
#include"SeqStack.h"
/*圖的鄰接表類型定義*/
typedef char VertexType[];
typedef int InfoPtr;            /*定義為整型,為了存放權值*/
typedef int VRType;
#define MaxSize 50              /*最大頂點個數*/
typedef enum{DG,DN,UG,UN}GraphKind;     /*圖的類型:有向圖、有向網、無向圖和無向網*/
typedef struct ArcNode          /*邊結點的類型定義*/
{
    int adjvex;                 /*弧指向的頂點的位置*/
    InfoPtr *info;              /*弧的權值*/
    struct ArcNode *nextarc;    /*訓示下一個與該頂點相鄰接的頂點*/
}ArcNode;
typedef struct VNode            /*頭結點的類型定義*/
{
    VertexType data;            /*用于存儲頂點*/
    ArcNode *firstarc;          /*訓示第一個與該頂點鄰接的頂點*/
}VNode,AdjList[MaxSize];
typedef struct                  /*圖的類型定義*/
{
    AdjList vertex;
    int vexnum,arcnum;          /*圖的頂點數目與弧的數目*/
    GraphKind kind;             /*圖的類型*/
}AdjGraph;
           
  • 有向網的拓撲排序
int ve[MaxSize];                /*ve存放事件最早發生時間*/
int TopologicalOrder(AdjGraph N,SeqStack *T)
/*采用鄰接表存儲結構的有向網N的拓撲排序,并求各頂點對應事件的最早發生時間ve*/
/*如果N無回路,則用用棧T傳回N的一個拓撲序列,并傳回1,否則為0*/
{
    int i,k,count=;
    int indegree[MaxSize];      /*數組indegree存儲各頂點的入度*/
    SeqStack S;
    ArcNode *p;
    /*将圖中各頂點的入度儲存在數組indegree中*/
    for(i=;i<N.vexnum;i++)     /*将數組indegree賦初值*/
        indegree[i]=;
    for(i=;i<N.vexnum;i++)
    {
        p=N.vertex[i].firstarc;
        while(p!=NULL)
        {
            k=p->adjvex;
            indegree[k]++;
            p=p->nextarc;
        }
    }
    InitStack(&S);              /*初始化棧S*/
    printf("拓撲序列:");
    for(i=;i<N.vexnum;i++)
        if(!indegree[i])        /*将入度為零的頂點入棧*/
            PushStack(&S,i);
        InitStack(T);           /*初始化拓撲序列頂點棧*/
        for(i=;i<N.vexnum;i++) /*初始化ve*/
            ve[i]=;
        while(!StackEmpty(S))   /*如果棧S不為空*/
        {
            PopStack(&S,&i);    /*從棧S将已拓撲排序的頂點j彈出*/
            printf("%s ",N.vertex[i].data);
            PushStack(T,i);     /*j号頂點入逆拓撲排序棧T*/
            count++;            /*對入棧T的頂點計數*/
            for(p=N.vertex[i].firstarc;p;p=p->nextarc)  /*處理編号為i的頂點的每個鄰接點*/
            {
                k=p->adjvex;            /*頂點序号為k*/
                if(--indegree[k]==)    /*如果k的入度減1後變為0,則将k入棧S*/
                    PushStack(&S,k);
                if(ve[i]+*(p->info)>ve[k]) /*計算頂點k對應的事件的最早發生時間*/
                    ve[k]=ve[i]+*(p->info);
            }
        }
        if(count<N.vexnum)
        {
            printf("該有向網有回路\n");
            return ;
        }
        else
            return ;
}
           
  • 有向網的關鍵路徑
int CriticalPath(AdjGraph N)
/*輸出N的關鍵路徑*/
{
    int vl[MaxSize];                /*事件最晚發生時間*/
    SeqStack T;
    int i,j,k,e,l,dut,value,count,e1[MaxSize],e2[MaxSize];
    ArcNode *p;
    if(!TopologicalOrder(N,&T))     /*如果有環存在,則傳回0*/
        return ;
    value=ve[];
    for(i=;i<N.vexnum;i++)
        if(ve[i]>value)
            value=ve[i];            /*value為事件的最早發生時間的最大值*/
        for(i=;i<N.vexnum;i++)     /*将頂點事件的最晚發生時間初始化*/
            vl[i]=value;
        while(!StackEmpty(T))       /*按逆拓撲排序求各頂點的vl值*/
            for(PopStack(&T,&j),p=N.vertex[j].firstarc;p;p=p->nextarc)
            /*彈出棧T的元素,賦給j,p指向j的後繼事件k*/
            {
                k=p->adjvex;
                dut=*(p->info);     /*dut為弧<j,k>的權值*/
                if(vl[k]-dut<vl[j]) /*計算事件j的最遲發生時間*/
                    vl[j]=vl[k]-dut;
            }
        printf("\n事件的最早發生時間和最晚發生時間\ni ve[i] vl[i]\n");
        for(i=;i<N.vexnum;i++)     /*輸出頂點對應的事件的最早發生時間最晚發生時間*/
            printf("%d   %d     %d\n",i,ve[i],vl[i]);
        printf("關鍵路徑為:(");
        for(i=;i<N.vexnum;i++)     /*輸出關鍵路徑經過的頂點*/
            if(ve[i]==vl[i])
                printf("%s ",N.vertex[i].data);
        printf(")\n");
        count=;
        printf("活動最早開始時間和最晚開始時間\n   弧    e   l   l-e\n");
        for(j=;j<N.vexnum;j++)     /*求活動的最早開始時間e和最晚開始時間l*/
            for(p=N.vertex[j].firstarc;p;p=p->nextarc)
            {
                k=p->adjvex;
                dut=*(p->info);     /*dut為弧<j,k>的權值*/
                e=ve[j];            /*e就是活動<j,k>的最早開始時間*/
                l=vl[k]-dut;        /*l就是活動<j,k>的最晚開始時間*/
                printf("%s→%s %3d %3d %3d\n",N.vertex[j].data,N.vertex[k].data,e,l,l-e);
                if(e==l)            /*将關鍵活動儲存在數組中*/
                {
                    e1[count]=j;
                    e2[count]=k;
                    count++;
                }
            }
        printf("關鍵活動為:");
        for(k=;k<count;k++)        /*輸出關鍵路徑*/
        {
            i=e1[k];
            j=e2[k];
            printf("(%s→%s) ",N.vertex[i].data,N.vertex[j].data);
        }
        printf("\n");
    return ;
}
           
  • 有向網的建立
int LocateVertex(AdjGraph G,VertexType v)
/*傳回圖中頂點對應的位置*/
{
    int i;
    for(i=;i<G.vexnum;i++)
        if(strcmp(G.vertex[i].data,v)==)
            return i;
        return -;
}
void CreateGraph(AdjGraph *N)
/*采用鄰接表存儲結構,建立有向網N*/
{
    int i,j,k,w;
    VertexType v1,v2;                   /*定義兩個弧v1和v2*/
    ArcNode *p;
    printf("請輸入圖的頂點數,邊數(以逗号分隔): ");
    scanf("%d,%d",&(*N).vexnum,&(*N).arcnum);
    printf("請輸入%d個頂點的值:",N->vexnum);
    for(i=;i<N->vexnum;i++)            /*将頂點存儲在頭結點中*/
    {
        scanf("%s",N->vertex[i].data);
        N->vertex[i].firstarc=NULL;     /*将相關聯的頂點置為空*/
    }
    printf("請輸入弧尾、弧頭和權值(以空格作為分隔):\n");
    for(k=;k<N->arcnum;k++)            /*建立邊連結清單*/
    {
        scanf("%s%s%*c%d",v1,v2,&w);
        i=LocateVertex(*N,v1);
        j=LocateVertex(*N,v2);
        /*j為弧頭i為弧尾建立鄰接表*/
        p=(ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex=j;
        p->info=(InfoPtr*)malloc(sizeof(InfoPtr));
        *(p->info)=w;
        /*将p指向的結點插入到邊表中*/
        p->nextarc=N->vertex[i].firstarc;
        N->vertex[i].firstarc=p;
    }
    (*N).kind=DN;
}
           
  • 有向網的輸出
void DisplayGraph(AdjGraph N)
/*網的鄰接矩陣N的輸出*/
{
    int i;
    ArcNode *p;
    printf("該網中有%d個頂點:",N.vexnum);
    for(i=;i<N.vexnum;i++)
        printf("%s ",N.vertex[i].data);
    printf("\n網中共有%d條弧:\n",N.arcnum);
    for(i=;i<N.vexnum;i++)
    {
        p=N.vertex[i].firstarc;
        while(p)
        {
            printf("<%s,%s,%d> ",N.vertex[i].data,N.vertex[p->adjvex].data,*(p->info));
            p=p->nextarc;
        }
        printf("\n");
    }
}
           
  • 有向網的銷毀
void DestroyGraph(AdjGraph *N)
/*銷毀無向圖G*/
{
    int i;
    ArcNode *p,*q;
    for(i=;i<N->vexnum;++i)        /*釋放網中的邊表結點*/
    {
        p=N->vertex[i].firstarc;    /*p指向邊表的第一個結點*/
        if(p!=NULL)                 /*如果邊表不為空,則釋放邊表的結點*/
        {
            q=p->nextarc;
            free(p);
            p=q;
        }
    }
    (*N).vexnum=;                  /*将頂點數置為0*/
    (*N).arcnum=;                  /*将邊的數目置為0*/
}
           
  • 主程式
void main()
{
    AdjGraph N;
    CreateGraph(&N);        /*采用鄰接表存儲結建構立有向網N*/
    DisplayGraph(N);        /*輸出有向網N*/
    CriticalPath(N);        /*求網N的關鍵路徑*/
    DestroyGraph(&N);       /*銷毀網N*/
}
           
  • 測試結果
【筆記】AOE網與關鍵路徑1.AOE網2.關鍵路徑3.求關鍵路徑的算法實作
【筆記】AOE網與關鍵路徑1.AOE網2.關鍵路徑3.求關鍵路徑的算法實作

繼續閱讀