您的位置:首页 > 其它

求AOE网的关键路径

2013-10-11 22:05 741 查看
转自http://blog.csdn.net/jkay_wong/article/details/6696701

以边表示活动,以顶点表示事件的有向网称为AOE(activity on edge)网.AOE网是一个

有向无环图,权值表示活动持续的时间。可以用AOE网来估计工程完成的时间。由于工程

只有一个开始点和一个完成点,所以在无环路的条件下,网中只有一个入度为0的点和一

个出度为0的点.

下面是几个和AOE网有关的概念:

(1)路径长度:路径上各个活动的持续时间之和

(2)完成工程的最短时间:由于AOE网中有活动是并行进行的,所以完成工程的最短时间

就是从开始点到完成点的最长路劲长度。

(3)活动最早开始时间(earlist time)(e(i)):从开始点到顶点vi的最长路径称为事件vi的最早发生时间,

这个时间决定了以vi为尾的弧表示的活动的最早开始时间.

(4)活动最晚开始时间(latest time)(l(i)):在不推迟整个工程完成的前提下,活动最迟开始的时间

(5)完成活动的时间余量:该活动的最迟开始时间减去最早开始时间

(6)关键路径(critical path):路径长度最长的路径称为关键路径

(7)关键活动(critical activity):关键路径上的活动称为关键活动,关键活动的特点是:e(i)=l(i)

分析关键路径的目的就是辨别在整个工程中哪些是关键活动,以便争取提高关键活动的工作

效率,缩短整个工程的工期。

文件"aoe.h"

#include<iostream>  

#include<string>  

#include<stack>  

#include<iomanip>  

using namespace std;  

  

const int MAX_VEX_NUM=20;  

int ve[20];//全局变量,存放各个事件的最早发生时间  

  

class ArcNode //表结点  

{  

public:  

    int adjvex;  

    int info;//权值  

    ArcNode *nextarc;  

};  

  

class VNode //头结点  

{  

public:  

    string data;  

    int indegree; //顶点的入度  

    ArcNode *firstarc;  

};  

  

class ALGraph   

{  

private:  

    VNode vertices[MAX_VEX_NUM];  

    int arcnum;  

    int vexnum;  

public:  

  

    void Create_ALG()  

    {  

        //构造有向网       

        string v1,v2;  

        int i,j,w;  

        ArcNode *p=NULL;  

  

        cout<<"输入顶点数和边数:";  

        cin>>vexnum>>arcnum;  

  

        cout<<"输入顶点名称:";  

        for(i=0;i<vexnum;i++)  

        {  

            cin>>vertices[i].data;  

            vertices[i].firstarc=NULL;  

            vertices[i].indegree=0;  

        }  

  

        for(int k=0;k<arcnum;k++)  

        {  

            cout<<"按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:";  

            cin>>v1>>v2>>w;  

            i=Locate_Vex(v1);  

            j=Locate_Vex(v2);  

  

            while(i==-1 || j==-1)  

            {  

                cout<<"输入的顶点错误,重新输入:";  

                cin>>v1>>v2;  

                i=Locate_Vex(v1);  

                j=Locate_Vex(v2);  

            }  

  

            p=new ArcNode;  

            p->adjvex=j;  

            p->info=w;  

            p->nextarc=vertices[i].firstarc;  

            vertices[i].firstarc=p;  

            vertices[j].indegree+=1; //作为有向弧的头的顶点入度加1  

        }  

  

        cout<<"有向网构造完成"<<endl;  

    }  

  

    int Locate_Vex(string v) //求顶点在顶点数组中的位置  

    {  

        for(int k=0;k<vexnum && vertices[k].data!=v;k++);  

        if(k<vexnum)  

            return k;  

        else  

            return -1;  

    }  

                                /*关键路径求解思想*/  

            /*--------------------------------------------------------------- 

            / 辨别关键路径就是要找出l(i)=e(i)的活动,为了求AOE网中的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>); 

            / 求事件的vj的最早发生时间ve(j)和最迟发生时间vl(j)要分两步进行: 

            / (1):从ve(0)=0(假设顶点0是开始点)开始,根据下面公式计算其它事件 

            / 的最早开始时间: ve(j)=Max{ve(i)+dut(<i,j>)} 其中i是j的所有直接前驱的集合 

            / (2)从vl(n-1)=ve(n-1)开始,根据下面公式计算其他事件的最晚开始时间: 

            / vl(i)=Min{vl(j)-dut(<i,j>)} 其中j是i的直接后继的集合 

            / 上述两个公式必须分别在拓扑有序和逆拓扑有序的前提下进行,也就是说ve(j-1) 

            / 必须在vj全部直接前驱的最早发生时间都求得以后才能确定。而vl(j-1)则必须在 

            / vj的所有直接后继的最晚发生时间求得之后才能确定,因此可以在拓扑排序的基础 

            / 上计算所有事件的ve(j-1)和vl(j-1). 

            / 为了能按逆拓扑有序序列的顺序计算各个顶点的vl值,需记下在拓扑排序的过程中 

            / 求得的拓扑有序序列,这只需要增加多一个栈,用来存储拓扑有序序列即可. 

            / 由于栈的结构特点,拓扑有序序列出栈就变成逆拓扑有序序列了. 

            /----------------------------------------------------------------*/  

  

    //求所有事件的最早发生时间  

    bool Topo_Order(stack<int> &T)  

    {  

        stack<int> s;  

        ArcNode *p=NULL;  

        for(int i=0;i<vexnum;i++)  

            if(!vertices[i].indegree)  

                s.push(i);  

        int count=0;  

        for(i=0;i<vexnum;i++)  

            ve[i]=0; //设各顶点最早发生为0  

        while(!s.empty())  

        {  

            int k=s.top();  

            s.pop();  

            T.push(k);  

            count++;  

            for(p=vertices[k].firstarc;p;p=p->nextarc)  

            {  

                int w=p->adjvex;  

                if(vertices[w].indegree)  

                    vertices[w].indegree--;  

                if(!vertices[w].indegree)  

                    s.push(w);  

                if(ve[k]+p->info>ve[w]) //求ve[w]的最早发生时间  

                    ve[w]=ve[k]+p->info;  

            }  

        }  

        if(count<vexnum)  

            return 0;  

        else  

            return 1;  

    }  

      

    //求所有事件的最晚发生时间并求出关键活动和关键路径  

    void Critical_Path()  

    {  

        stack<int> T;  

        string cp[10];  

        int c=0;  

        if(!Topo_Order(T))  

        {  

            cout<<"该有向网有环!"<<endl;  

            return;  

        }  

        int vl[20];  

        for(int i=0;i<vexnum;i++)  

            vl[i]=ve[vexnum-1]; //初始化顶点事件最迟发生时间  

          

        while(!T.empty())  

        {  

            ArcNode *p=NULL;  

            int j=T.top();  

            T.pop();  

            for(p=vertices[j].firstarc;p;p=p->nextarc)  

            {  

                int k=p->adjvex;  

                int dut=p->info;  

                if(vl[k]-dut<vl[j])  

                    vl[j]=vl[k]-dut;  

            }  

        }  

        //下面是根据关键活动的特点(最早开始时间和最晚开始时间相等)求关键活动,做上标记  

        cout<<"tail  head  weight  earliest time  latest time  tag "<<endl;  

        for(int j=0;j<vexnum;j++)  

            for(ArcNode *p=vertices[j].firstarc;p;p=p->nextarc)  

            {  

                int k=p->adjvex;  

                int dut=p->info;  

                int ee=ve[j];  

                int el=vl[k]-dut;  

                char tag;  

                tag=(ee==el)?'*':' ';  

                cout<<setw(3)<<vertices[j].data<<setw(6)<<vertices[k].data<<setw(7)<<dut<<setw(9)<<ee<<setw(16)<<el<<setw(9)<<tag<<endl;  

                if(tag=='*' && j==0)  

                {  

                    cp[c]=vertices[j].data;  

                    c++;  

                    cp[c]=vertices[k].data;  

                    c++;  

                }  

                else if(tag=='*' && j!=0)  

                {  

                    cp[c]=vertices[k].data;  

                    c++;  

                }             

            }  

        cout<<"The critical activities are ended with * "<<endl;  

        cout<<"So the critical path is :";  

        for(i=0;i<c-1;i++)  

            cout<<cp[i]<<"->";  

        cout<<cp[c-1]<<endl;  

    }  

  

};  

主函数"main.cpp"

#include"aoe.h"  

  

int main()  

{  

    ALGraph G;  

    G.Create_ALG();  

    G.Critical_Path();  

    cout<<endl;  

    return 0;  

}  

输入和输出结果:

输入顶点数和边数:6 8  

输入顶点名称:v1 v2 v3 v4 v5 v6  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v1 v2 3  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v1 v3 2  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v2 v5 3  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v2 v4 2  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v3 v4 4  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v3 v6 3  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v4 v6 2  

按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v5 v6 1  

有向网构造完成  

tail  head  weight  earliest time  latest time  tag  

 v1    v3      2        0               0        *  

 v1    v2      3        0               1  

 v2    v4      2        3               4  

 v2    v5      3        3               4  

 v3    v6      3        2               5  

 v3    v4      4        2               2        *  

 v4    v6      2        6               6        *  

 v5    v6      1        6               7  

The critical activities are ended with *  

So the critical path is :v1->v3->v4->v6  

  

Press any key to continue  

根据输入所生成的有向网如下所示:



当关键路径只有一条时,输出关键路径是对的,当关键路径不知一条时就是错的,但是依然是可以找出所有的关键活动,路径输出的算法,以后会完善到可以输出所以关键路径

求逆拓扑有序序列 除了利用求拓扑有序时进栈外,还可以直接用DFS遍历该有向图,直到该结点的所有邻接结点都输出之后,才将其输出,这个序列就是逆拓扑有序序列
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法