结构推理 采用图的邻接矩阵表示方法,实现拓扑排序和关键路径的求解过程。使用实现的算法对于下图所示的AOE网,求出各活动的可能的最早开始时间和最晚开始时间。输出整个工程的最短完成时间是多少?哪些活动是关键活动?说明哪项活动提高速度后能导致整个工程提前完成。
【正确答案】(1)数据结构
   typedef int Vexfype;
   typedef int AdjType;
   typedef struct{
       VexType vexs[MAXVEX];    /*顶点信息*/
       adjType arcs[MAXVEX][MAXVEX];  /*边信息*/
       int n;    /*图的顶点个数*/
   }GraphMatrix;
   typedef struct{
       VexType vexs[MAXVEX];    /*顶点信息*/
       int vexsno[MAXVEX];    /*顶点在顶点表中的下标值*/
   }Topo;
   (2)思路
   按照邻接矩阵表示法创建出邻接矩阵,矩阵中第i行第j列存储由顶点i到顶点j的活动的权值aij,若不存在相应边,则aij=0。先用拓扑排序法确定AOE网的一个拓扑序列,再根据确定好的序列分别计算出各个顶点的最早可能发生时间、最迟允许发生时间和各个活动的最早开始时间、最迟开始时间。通过判断活动的最早开始时间和最迟开始时间,可以确定AOE网的关键路径。
   (3)算法
   void findlnDegree(GraphMatrix*paoe,int*inDegree){
   /*求出图中所有顶点的入度,方法是搜索整个邻接表*/
       inti,j;
       for(i=0;i<paoe->n;i++)
           inDegree[i]=O;
       for(i=0;i<paoe->n;i++)
           for(j=0;j<paoe->n;j++)
               if(j!=i&&paoe->arcs[j][i]!=MAX)++inDegree[i];
   }
   int topoSort(GraphMatrix*paoe,Topo*ptopo){
       int i,j,k,nodeno=0,top=-1;
       int indegree[MAXVEX];
       findInDegree(paoe,indegree);
       for(i=0;i<paoe->n;i++)
         if(indegree[i]==0){    /*将入度为零的顶点入栈*/
           indegree[i]=top;
           top=i;
         }
       while(top!=-1){    /*栈不为空*/
         j=top;
         top=indegree[top];    /*取出当前栈硕元素*/
         ptopo->vexs[nodeno]=paoe->vexs[j];/*将该元素输出到拓扑序列中*/
         ptopo->Vexsno[nodeno++]=j;
         for(i=0;i<paoe->n;i++){    /*删除以该顶点为起点的边*/
           if(i!=j&&paoe->arcs[j][i]!=MAX){
             k=i;
             indegree[k]--;
             if(indegree[k]==0){    /*将新的入度为零的边入栈*/
               indegree[k]=top;
               top=k;
             }
           }
       }
   }
   if(nodeno<paoe->n){    /*AOV网中存在回路*/
       printf("The aov network has a cycle\n");
       return FALSE;
   }
   return TRUE;
   }
   int CriticalPath(GraphMatrix*paoe){
       int i,j,k;
       AdjType ee[MAXVEX],le[MAXVEX],1[MAXEDGE],e[MAXEDGE];
       Topotopo;
       if(topoSort(paoe,&topo)==FALSE)   /*求AOE网的一个拓扑序列*/
           return FALSE;
       for(i=0;i<paoe->n;i++)
           ee[i]=0;
       for(k=0;k<paoe->n;k++){
       /*求事件vj可能的最早发生时间ee(j)*/
           i=topo.vexsno[k];
           for(j=0;j<paoe->n;j++)
             if(j!=i&&paoe->arcs[i][j]<MAX&&ee[i]+paoe->arcs[i][j]>ee[j]])
               ee [j]=ee[i]+paoe->arcs[i][j];
   }
   for(i=0;i<paoe->n;i++)  /*求事件vi允许的最迟发生时间le(i)*/
       le[i]=ee[paoe->n-1];
   for(k=paoe->n-2;k>=0;k--){
       i=topo.vexsno[k];
       for(j=0;j<paoe->n;j++)
         if(j!=i&&paoe->arcs[i][j]<MAX&&le[j]-paoe->arcs[i][j]<le[i])
           le[i]=le[j]-paoe->arcs[i][j];
   }
   k=0:
   printf("k<start,end>weight e(k)l(k)    state\n");
   for(i=0;i<paoe->n;i++)
   /*求活动ak的最早开始时间e(k)及最晚开始时间l(k)*/
   for(j=0;j<paoe->n;j++)
       if(j!=i&&paoe->arcs[i][j]<MAX){
         e[k]=ee[i];
         l[k]=le[j]-paoe->arcs[i][j];
       printf("/%2d<v/%d,v/%d>/%6d/%4d/%4d",k,i,j,
           paoe->arcs[i][j],e[k],l[k]);
           if(e[k]==l[k])
               printf("Critical");
               printf("\n");
               k++:
         }
       printf("\n");
       printf("Minimum project time cost:/%d\n",le[paoe->n-1]);
       return TRUE;
   }
   (4)代价分析
   1)拓扑排序算法
   算法首先检查入度为0的顶点,并将这些顶点压入栈,花费的时间为O(n2)。然后,在进行拓扑排序时,每个顶点都入栈一次,且邻接矩阵中每个元素都被检查一遍,运行时间为O(n2)。因此,拓扑排序算法的时间复杂度为O(n2)。
   2)求关键路径算法
   在求事件可能的最早发生时间及允许的最迟发生时间,以及活动的最早开始时间和最晚开始时间时,都要对图邻接矩阵中每个元素检查一遍,时间花费为O(n2)。因此,求关键路径算法的时间代价为O(n2)。
   总的时间代价为O(n2)。
【答案解析】该调试结果与采用邻接表表示法时的调试结果完全相同。
   调试结果中列出了各活动的可能的最早开始时间和最晚开始时间。
   从调试结果中可以看出:
   ·整个工程的最短完成时间是24。
   ·活动<V0,V2>,<V2,V3>,<V3,V6>,<V6,V8>,<V8,V9>是关键活动。
   ·活动<V0,V2>,<V2,V3>,<V3,V6>,<V6,V8>,<V8,V9>提高速度后能导致整个工程提前完成。