zoukankan      html  css  js  c++  java
  • 图->有向无环图->求关键路径

    文字描述

      与AOV-网相对应的是AOE-网(Activity on Edge)即边表示活动的网。AOE-网是一个带权的有向无环图。其中,顶点表示事件Event,弧表示活动,权表示活动持续的时间。通常,AOE-网可用来估算工程的完成时间。

      对AOE-网来说,研究的问题有两个:(1)完成整项工程至少需要多少时间?(2)哪些活动是影响工程进度的关键?  

      由于在AOE-网中有些活动可以并行地进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度(指路径上各活动持续时间之和,不是路径上弧的数目)。路径长度最长的路径叫做关键路径。

      假设开始点是v1,从v1到vi的最长路径叫事件vi的最早发生时间。这个时间决定了所有以vi为尾的弧所表示的活动的最早开始时间。用e(i)表示活动ai的最早开始时间。用l(i)表示ai的最迟开始时间,这是在不推迟整个工程完成的前提下,活动ai最迟必须开始进行的时间。两者之差l(i)-e(i)表示活动ai的时间余量。我们把l(i)==e(i)的活动叫做关键活动。

      显然,关键路径上的所有活动都是关键活动,因此提前完成非关键活动并不能加快工程的进度。

      那么如何求得各个活动的最早开始时间e(i)和最晚开始时间l(i)呢?首先应求得事件的最早发生时间ve(j)和最迟发生时间vl(j)。如果活动ai由弧<j,k>表示,其持续时间记为dut(<j,k>),则有如下关系:

      e(i) = ve(j)

      l(i) = vl(k) – dut(<j,k>)

      求ve(j)和vl(j)需分两步进行:

        (1)   从ve(0)=0开始向前递推

        

        (2)   从vl(n-1)=ve(n-1)起向后递推

        

        这两个递推公式可以利用之前的拓扑排序算法求得。

    示意图

    算法分析

      算法复杂度同拓扑排序算法,为O(n+e)。

    代码实现

      1 //
      2 // Created by lady on 18-12-29.
      3 //
      4 
      5 #include <stdlib.h>
      6 #include <stdio.h>
      7 #define MAX_VERTEX_NUM 20 //最大顶点数
      8 #define MAX_EDGE_NUM 50 //最大弧数
      9 typedef enum {DG,DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网}
     10 typedef struct ArcNode{
     11     int adjvex;    //该弧所指向的顶点的位置
     12     struct ArcNode *nextarc;    //指向下一条弧的指针
     13     int info;    //该弧相关信息的指针
     14 }ArcNode;
     15 typedef struct VNode{
     16     char data[10];//顶点信息
     17     ArcNode *firstarcIN;//第一条以该顶点为弧头的弧结点,其他顶点->该结点
     18     ArcNode *firstarcOUT;//第一条以该顶点为弧尾的弧结点,该结点->其他顶点
     19 }VNode, AdjList[MAX_VERTEX_NUM];
     20 typedef struct{
     21     AdjList vertices;
     22     int vexnum;//图的顶点数
     23     int arcnum;//图的弧数
     24     int kind; //图的种类标志
     25 }ALGraph;
     26 
     27 //根据顶点信息,返回该顶点在图中的位置坐标。
     28 int LocateVex(ALGraph *G, char data[])
     29 {
     30     int i = 0;
     31     for(i=0; i<G->vexnum; i++){
     32         if(!strncmp(G->vertices[i].data, data, strlen(G->vertices[i].data))){
     33             return i;
     34         }
     35     }
     36     return -1;
     37 }
     38 
     39 //利用头插法,在弧结点链表头部,插入位置v的弧结点
     40 int InsFirst(ArcNode *L, int v, int weight)
     41 {
     42     if((L==NULL) || (v<0)){
     43         return -1;
     44     }
     45     ArcNode *n = (ArcNode *)malloc(sizeof(struct ArcNode));
     46     n->adjvex = v;
     47     n->nextarc = L->nextarc;
     48     n->info = weight;
     49     L->nextarc = n;
     50     return 0;
     51 }
     52 
     53 //采用邻接表存储方法,创建有向网,即带权的有向图
     54 int CreateDN(ALGraph *G)
     55 {
     56     printf("开始创建一个有向图,请输入顶点数,弧数:");
     57     int i = 0, j = 0, k = 0;
     58     char v1[10] = {0}, v2[10]={0}, info[10] = {0};
     59     char tmp[20] = {0};
     60     G->kind = DN;
     61     scanf("%d,%d", &G->vexnum, &G->arcnum);
     62     for(i=0; i<G->vexnum; i++){
     63         printf("输入第%d个顶点: ", i+1);
     64         memset(G->vertices[i].data, 0, sizeof(G->vertices[i].data));
     65         scanf("%s", G->vertices[i].data);
     66         G->vertices[i].firstarcOUT = (struct ArcNode *)malloc(sizeof(struct ArcNode));
     67         G->vertices[i].firstarcOUT->adjvex = -1;
     68         G->vertices[i].firstarcOUT->nextarc = NULL;
     69         G->vertices[i].firstarcIN = (struct ArcNode *)malloc(sizeof(struct ArcNode));
     70         G->vertices[i].firstarcIN->adjvex = -1;
     71         G->vertices[i].firstarcIN->nextarc = NULL;
     72     }
     73     for(k=0; k<G->arcnum; k++)
     74     {
     75         printf("输入第%d条弧(顶点1, 顶点2, 权值): ", k+1);
     76         memset(tmp, 0, sizeof(tmp));
     77         scanf("%s", tmp);
     78 //      sscanf(tmp, "%[^','],%s[^\n]", v1, v2);
     79         sscanf(tmp, "%[^','],%[^','],%s[^\n]", v1, v2, info);
     80         i = LocateVex(G, v1);
     81         j = LocateVex(G, v2);
     82         if(i<0 || j<0){
     83             printf("<%s,%s> is a invalid arch!
    ", v1, v2);
     84             return -1;
     85         }
     86         InsFirst(G->vertices[i].firstarcOUT, j, atoi((const char *)info));
     87         InsFirst(G->vertices[j].firstarcIN, i, atoi((const char *)info));
     88     }
     89     return 0;
     90 }
     91 
     92 
     93 void printG(ALGraph *G)
     94 {
     95     printf("
    ");
     96     if(G->kind == DG){
     97         printf("类型:有向图;顶点数 %d, 弧数 %d
    ", G->vexnum, G->arcnum);
     98     }else if(G->kind == DN){
     99         printf("类型:有向网;顶点数 %d, 弧数 %d
    ", G->vexnum, G->arcnum);
    100     }else if(G->kind == UDG){
    101         printf("类型:无向图;顶点数 %d, 弧数 %d
    ", G->vexnum, G->arcnum);
    102     }else if(G->kind == UDN){
    103         printf("类型:无向网;顶点数 %d, 弧数 %d
    ", G->vexnum, G->arcnum);
    104     }
    105     int i = 0;
    106     ArcNode *p = NULL;
    107     printf("邻接表:
    ");
    108     for(i=0; i<G->vexnum; i++){
    109         printf("(%d,%s)	", i,G->vertices[i].data);
    110         p = G->vertices[i].firstarcOUT;
    111         while(p){
    112             if(p->adjvex >= 0)
    113                 printf("(%d,%s) %d	", p->adjvex, G->vertices[p->adjvex].data, p->info);
    114             p = p->nextarc;
    115         }
    116         printf("
    ");
    117     }
    118     printf("逆邻接表:
    ");
    119     for(i=0; i<G->vexnum; i++){
    120         printf("(%d,%s)	", i,G->vertices[i].data);
    121         p = G->vertices[i].firstarcIN;
    122         while(p){
    123             if(p->adjvex >= 0)
    124                 printf("(%d,%s) %d	", p->adjvex, G->vertices[p->adjvex].data, p->info);
    125             p = p->nextarc;
    126         }
    127         printf("
    ");
    128     }
    129     return;
    130 }
    131 
    132 
    133 #define STACK_INIT_SIZE 20  //栈的初始分配量大小
    134 #define STACK_INCREMENT 5   //栈容量不足时需新增的容量大小
    135 typedef struct {
    136     int *base;  //指向栈底指针
    137     int *top;   //指向栈顶指针
    138     int stacksize;  //栈的当前容量大小
    139 }SqStack;
    140 int InitStack(SqStack *s);  //初始化一个栈
    141 int StackEmpty(SqStack *s); //判断栈是否为空
    142 int Push(SqStack *S, int *e);  //入栈函数
    143 int Pop(SqStack *S, int *e);    //出栈函数
    144 
    145 //算法各个顶点的入度,并将结果存放在indegree数组中
    146 int FindInDegree(ALGraph *G, int indegree[])
    147 {
    148     printf("
    对各个顶点求入度...
    ");
    149     int i = 0;
    150     ArcNode *p = NULL;
    151     for(i=0; i<G->vexnum; i++) {
    152         p = G->vertices[i].firstarcIN;
    153         while (p) {
    154             if (p->adjvex >= 0) {
    155                 indegree[i] += 1;
    156             }
    157             p = p->nextarc;
    158         }
    159     }
    160     for(i=0; i<G->vexnum; i++){
    161         printf("(%d,%s)的入度为%d
    ", i, G->vertices[i].data, indegree[i]);
    162     }
    163     return 0;
    164 }
    165 int ve[MAX_EDGE_NUM] = {0};
    166 int vl[MAX_EDGE_NUM] = {0};
    167 
    168 int ToplogicalSort(ALGraph *G, SqStack *T)
    169 {
    170     int i = 0;
    171     int j = 0;
    172     int k = 0;
    173     int count = 0;
    174     int indegree[MAX_VERTEX_NUM] = {0};
    175     ArcNode *p = NULL;
    176     SqStack S;
    177     //求各个顶点的入度
    178     FindInDegree(G, indegree);
    179     //初始化栈S,保存零入度顶点栈
    180     InitStack(&S);
    181     //将入度为0的顶点入栈S.
    182     for(i=0; i<G->vexnum; i++){
    183         if(!indegree[i]) {
    184             Push(&S, &i);
    185         }
    186     }
    187     //初始化栈T,为拓扑序列顶点栈
    188     InitStack(T);
    189     //初始化
    190     for(i=0; i<G->vexnum; i++){
    191         ve[i] = 0;
    192     }
    193     printf("
    进行拓扑排序:");
    194     while(StackEmpty(&S)){
    195         Pop(&S, &j);
    196         //j号顶点入T栈并计数
    197         Push(T, &j);
    198         ++count;
    199         printf("(%d,%s)	", j, G->vertices[j].data);
    200         //对j号顶点的每个邻接点的入度减1
    201         for(p=G->vertices[j].firstarcOUT; p; p=p->nextarc){
    202             k = p->adjvex;
    203             if(k<0){
    204                 continue;
    205             }
    206             //若入度为0,则入栈S
    207             if(!(--indegree[k])){
    208                 Push(&S, &k);
    209             }
    210             if(ve[j]+p->info > ve[k])
    211                 ve[k] = ve[j]+p->info;
    212         }
    213     }
    214     printf("
    ");
    215     if(count<G->vexnum){
    216         //该有向网有环
    217         return -1;
    218     }else{
    219         return 0;
    220     }
    221 }
    222 
    223 //G为有向图, 输出G的各项关键活动
    224 int CriticalPath(ALGraph *G)
    225 {
    226     SqStack T;
    227     if(ToplogicalSort(G, &T)<0){
    228         return -1;
    229     }
    230     int i = 0;
    231     int j = 0;
    232     int k = 0;
    233     int dut = 0;
    234     ArcNode *p = NULL;
    235     //初始化顶点时间的最迟发生时间
    236     for(i=0; i<G->vexnum; i++){
    237         vl[i] = ve[i];
    238     }
    239     //按照拓扑逆序求各顶点的vl值
    240     while(StackEmpty(&T)){
    241         Pop(&T, &j);
    242 
    243         for(p=G->vertices[j].firstarcOUT; p; p=p->nextarc){
    244             k = p->adjvex;
    245             if(k<0)
    246                 continue;
    247             dut = p->info; //dut(<j,k>)
    248             if(vl[k]-dut < vl[j])
    249                 vl[j] = vl[k] - dut;
    250         }
    251 
    252 
    253         for(p=G->vertices[j].firstarcIN; p; p=p->nextarc) {
    254             k = p->adjvex;
    255             if (k < 0)
    256                 continue;
    257             dut = p->info; //dut<k,j>
    258 
    259             if (vl[j] - dut > vl[k]) {
    260                 vl[k] = vl[j] - dut;
    261             }
    262         }
    263     }
    264     printf("
    输出各个顶点的最早发生时间ve和最晚发生时间vl
    ");
    265     for(i=0; i<G->vexnum; i++){
    266         printf("ve(%d,%s)=%d	", i, G->vertices[i].data, ve[i]);
    267         printf("vl(%d,%s)=%d
    ", i, G->vertices[i].data, vl[i]);
    268     }
    269     int ee = 0;
    270     int el = 0;
    271     char tag = 0;
    272     printf("
    输出各活动的最早发生时间ee和最晚发生时间el, *表示该活动为关键路径
    ");
    273     for(j=0; j<G->vexnum; j++){
    274         for(p=G->vertices[j].firstarcOUT; p; p=p->nextarc){
    275             k = p->adjvex;
    276             if(k<0){
    277                 continue;
    278             }
    279             dut = p->info;
    280             ee = ve[j];
    281             el = vl[k]-dut;
    282             tag = (ee==el)?'*':' ';
    283             //输出关键活动
    284             printf("(%d,%s)->(%d,%s), weight:%d, ee=%d, el=%d, tag=%c
    ", j, G->vertices[j].data, k, G->vertices[k].data, dut, ee, el, tag);
    285         }
    286     }
    287     return 0;
    288 }
    289 
    290 int main(int argc, char *argv[])
    291 {
    292     ALGraph G;
    293     //创建有向图
    294     if(CreateDN(&G)<0){
    295         printf("创建有向图时出错!
    ");
    296         return -1;
    297     }
    298     //打印图
    299     printG(&G);
    300     //求关键路径
    301     CriticalPath(&G);
    302     return 0;
    303 }
    304 
    305 
    306 int InitStack(SqStack *S){
    307     S->base = (int *) malloc(STACK_INIT_SIZE * sizeof(int));
    308     if(!S->base){
    309         return -1;
    310     }
    311     S->top = S->base;
    312     S->stacksize = STACK_INIT_SIZE;
    313     return 0;
    314 }
    315 
    316 int StackEmpty(SqStack *s){
    317     if(s->base == s->top){
    318         return 0;
    319     }else{
    320         return -1;
    321     }
    322 }
    323 
    324 int Push(SqStack *s, int *e){
    325     if((s->top-s->base) >= s->stacksize){
    326         s->base = (int*)realloc(s->base, (s->stacksize+STACK_INCREMENT)*(sizeof(int)));
    327         if(!s->base){
    328             return -1;
    329         }
    330         s->top = s->base + s->stacksize;
    331         s->stacksize += STACK_INCREMENT;
    332     }
    333     if(e == NULL){
    334         return -1;
    335     }else{
    336         *s->top = *e;
    337     }
    338     s->top += 1;
    339     return 0;
    340 }
    341 
    342 int Pop(SqStack *s, int *e)
    343 {
    344     if(s->top == s->base) {
    345         return -1;
    346     }else{
    347         s->top -=1;
    348         *e = *s->top;
    349         return 0;
    350     }
    351 }
    求有向无环网的关键路径

    代码运行

    /home/lady/CLionProjects/untitled/cmake-build-debug/untitled
    开始创建一个有向图,请输入顶点数,弧数:9,11
    输入第1个顶点: V1
    输入第2个顶点: V2
    输入第3个顶点: V3
    输入第4个顶点: V4
    输入第5个顶点: V5
    输入第6个顶点: V6
    输入第7个顶点: V7
    输入第8个顶点: V8
    输入第9个顶点: V9
    输入第1条弧(顶点1, 顶点2, 权值): V1,V2,6
    输入第2条弧(顶点1, 顶点2, 权值): V1,V3,4
    输入第3条弧(顶点1, 顶点2, 权值): V1,V4,5
    输入第4条弧(顶点1, 顶点2, 权值): V2,V5,1
    输入第5条弧(顶点1, 顶点2, 权值): V3,V5,1
    输入第6条弧(顶点1, 顶点2, 权值): V4,V6,2
    输入第7条弧(顶点1, 顶点2, 权值): V5,V7,9
    输入第8条弧(顶点1, 顶点2, 权值): V5,V8,7
    输入第9条弧(顶点1, 顶点2, 权值): V6,V8,4
    输入第10条弧(顶点1, 顶点2, 权值): V7,V9,2
    输入第11条弧(顶点1, 顶点2, 权值): V8,V9,4

    类型:有向网;顶点数 9, 弧数 11
    邻接表:
    (0,V1)    (3,V4) 5    (2,V3) 4    (1,V2) 6    
    (1,V2)    (4,V5) 1    
    (2,V3)    (4,V5) 1    
    (3,V4)    (5,V6) 2    
    (4,V5)    (7,V8) 7    (6,V7) 9    
    (5,V6)    (7,V8) 4    
    (6,V7)    (8,V9) 2    
    (7,V8)    (8,V9) 4    
    (8,V9)    
    逆邻接表:
    (0,V1)    
    (1,V2)    (0,V1) 6    
    (2,V3)    (0,V1) 4    
    (3,V4)    (0,V1) 5    
    (4,V5)    (2,V3) 1    (1,V2) 1    
    (5,V6)    (3,V4) 2    
    (6,V7)    (4,V5) 9    
    (7,V8)    (5,V6) 4    (4,V5) 7    
    (8,V9)    (7,V8) 4    (6,V7) 2    

    对各个顶点求入度...
    (0,V1)的入度为0
    (1,V2)的入度为1
    (2,V3)的入度为1
    (3,V4)的入度为1
    (4,V5)的入度为2
    (5,V6)的入度为1
    (6,V7)的入度为1
    (7,V8)的入度为2
    (8,V9)的入度为2

    进行拓扑排序:(0,V1)    (1,V2)    (2,V3)    (4,V5)    (6,V7)    (3,V4)    (5,V6)    (7,V8)    (8,V9)    

    输出各个顶点的最早发生时间ve和最晚发生时间vl
    ve(0,V1)=0    vl(0,V1)=0
    ve(1,V2)=6    vl(1,V2)=6
    ve(2,V3)=4    vl(2,V3)=6
    ve(3,V4)=5    vl(3,V4)=8
    ve(4,V5)=7    vl(4,V5)=7
    ve(5,V6)=7    vl(5,V6)=10
    ve(6,V7)=16    vl(6,V7)=16
    ve(7,V8)=14    vl(7,V8)=14
    ve(8,V9)=18    vl(8,V9)=18

    输出各活动的最早发生时间ee和最晚发生时间el, *表示该活动为关键路径
    (0,V1)->(3,V4), weight:5, ee=0, el=3, tag=
    (0,V1)->(2,V3), weight:4, ee=0, el=2, tag=
    (0,V1)->(1,V2), weight:6, ee=0, el=0, tag=*
    (1,V2)->(4,V5), weight:1, ee=6, el=6, tag=*
    (2,V3)->(4,V5), weight:1, ee=4, el=6, tag=
    (3,V4)->(5,V6), weight:2, ee=5, el=8, tag=
    (4,V5)->(7,V8), weight:7, ee=7, el=7, tag=*
    (4,V5)->(6,V7), weight:9, ee=7, el=7, tag=*
    (5,V6)->(7,V8), weight:4, ee=7, el=10, tag=
    (6,V7)->(8,V9), weight:2, ee=16, el=16, tag=*
    (7,V8)->(8,V9), weight:4, ee=14, el=14, tag=*

    Process finished with exit code 0

    /home/lady/CLionProjects/untitled/cmake-build-debug/untitled
    开始创建一个有向图,请输入顶点数,弧数:6,8
    输入第1个顶点: V1
    输入第2个顶点: V2
    输入第3个顶点: V3
    输入第4个顶点: V4
    输入第5个顶点: V5
    输入第6个顶点: V6
    输入第1条弧(顶点1, 顶点2, 权值): V1,V2,3
    输入第2条弧(顶点1, 顶点2, 权值): V1,V3,2
    输入第3条弧(顶点1, 顶点2, 权值): V2,V4,2
    输入第4条弧(顶点1, 顶点2, 权值): V2,V5,3
    输入第5条弧(顶点1, 顶点2, 权值): V3,V4,4
    输入第6条弧(顶点1, 顶点2, 权值): V3,V6,3
    输入第7条弧(顶点1, 顶点2, 权值): V4,V6,2
    输入第8条弧(顶点1, 顶点2, 权值): V5,V6,1

    类型:有向网;顶点数 6, 弧数 8
    邻接表:
    (0,V1)    (2,V3) 2    (1,V2) 3    
    (1,V2)    (4,V5) 3    (3,V4) 2    
    (2,V3)    (5,V6) 3    (3,V4) 4    
    (3,V4)    (5,V6) 2    
    (4,V5)    (5,V6) 1    
    (5,V6)    
    逆邻接表:
    (0,V1)    
    (1,V2)    (0,V1) 3    
    (2,V3)    (0,V1) 2    
    (3,V4)    (2,V3) 4    (1,V2) 2    
    (4,V5)    (1,V2) 3    
    (5,V6)    (4,V5) 1    (3,V4) 2    (2,V3) 3    

    对各个顶点求入度...
    (0,V1)的入度为0
    (1,V2)的入度为1
    (2,V3)的入度为1
    (3,V4)的入度为2
    (4,V5)的入度为1
    (5,V6)的入度为3

    进行拓扑排序:(0,V1)    (1,V2)    (4,V5)    (2,V3)    (3,V4)    (5,V6)    

    输出各个顶点的最早发生时间ve和最晚发生时间vl
    ve(0,V1)=0    vl(0,V1)=0
    ve(1,V2)=3    vl(1,V2)=4
    ve(2,V3)=2    vl(2,V3)=2
    ve(3,V4)=6    vl(3,V4)=6
    ve(4,V5)=6    vl(4,V5)=7
    ve(5,V6)=8    vl(5,V6)=8

    输出各活动的最早发生时间ee和最晚发生时间el, *表示该活动为关键路径
    (0,V1)->(2,V3), weight:2, ee=0, el=0, tag=*
    (0,V1)->(1,V2), weight:3, ee=0, el=1, tag=
    (1,V2)->(4,V5), weight:3, ee=3, el=4, tag=
    (1,V2)->(3,V4), weight:2, ee=3, el=4, tag=
    (2,V3)->(5,V6), weight:3, ee=2, el=5, tag=
    (2,V3)->(3,V4), weight:4, ee=2, el=2, tag=*
    (3,V4)->(5,V6), weight:2, ee=6, el=6, tag=*
    (4,V5)->(5,V6), weight:1, ee=6, el=7, tag=

    Process finished with exit code 0

  • 相关阅读:
    geoserver发布地图服务WMTS
    geoserver发布地图服务WMS
    geoserver安装部署步骤
    arcgis api 3.x for js 入门开发系列十四最近设施点路径分析(附源码下载)
    arcgis api 3.x for js 入门开发系列十三地图最短路径分析(附源码下载)
    cesium 之自定义气泡窗口 infoWindow 后续优化篇(附源码下载)
    arcgis api 3.x for js 入门开发系列十二地图打印GP服务(附源码下载)
    arcgis api 3.x for js 入门开发系列十一地图统计图(附源码下载)
    arcgis api 3.x for js 入门开发系列十叠加 SHP 图层(附源码下载)
    arcgis api 3.x for js入门开发系列九热力图效果(附源码下载)
  • 原文地址:https://www.cnblogs.com/aimmiao/p/10197259.html
Copyright © 2011-2022 走看看