图-----------拓扑排序+AOE网络关键路径

1.拓扑排序

(1)举个例子,要学习某些课程必须先学过一些课程

用图把这个东东描述出来就变成:

 

那么,问题来啦,是否可以找到一个序列,使得这个序列上的所有课程都满足:先修课程在后修的课程前面?这样的序列就是拓扑序列.....

(2)怎么求拓扑序列?

简单的说是不断去掉没有前驱的点,得到的这些点就是拓扑序列;

还是上面的例子:

step1:9没有前驱,去掉(和它相关的边也去掉);

step2:这时候有8,6,4,三个点没有前驱,随便选一个去掉(这个以随便就说明拓扑序列不唯一喔~)

......(下面,你懂的~)

(3)算法

要用到没有前驱所以要图的入度;

上面的模拟过程知道实际上是BFS:

a.建立入度为零的顶点排队
b.扫描顶点表,将入度为0的顶点入队;
c.while(排队不空)

{
输出队头结点;
记下输出结点的数目;
删去与之关联的出边;
若有入度为0的结点,入队
}

d.若输出结点个数小于总的顶点个数,则输出有环路;

(4)象征性的贴一小段代码

void topsort(Adgraph* G)
{
    queue Q;
    int x,count=0;
    for(int i=1; i<=G->n; i++)
        if(G->Ad[i].indegree==0) Q.push(i);//入度为0的顶点入队
    while(!Q.empty())
    {
        x=Q.front();
        Q.pop();
        cout << G->Ad[x].element;//输出点
        count++;//计数器++
        link *m=G->Ad[x].firstedge;
        while(m!=NULL)
        {
            if((--G->Ad[m->v].indegree)==0) Q.push(m->v) ;
			//每当去掉顶点,入度--;如果这时候它变成没有前驱的顶点,入队
            m=m->next;
        }
    }
    if (countn) cout<<"图中有环路" << endl;
}


2.AOE网络

(1)AOE是什么东东?

a. AOE上顶点表示事件,边表示活动,边上的权值表示活动需要的时间,入度为0的点叫做源点(V1),出度为0的点叫做结束点(v9);

b.我们要解决的问题:从源点到达结束点经过的活动的最大(最大喔!)时间,比如上面的红线部分就是完成最大花费时间,关键路径就是这条长度最长的路径(a1->a4->a7->a10或者a1->a4->a8->a11[关键路径不唯一]

(2)问题怎么求解?

a.事件(eVent)的最早(Early)发生时间---源点到这个点的最长路径---VE[j];

b.事件(eVent)的最迟(Late)发生时间---在保证汇点Vn在VE(n)时刻完成的前提下,事件Vk的允 许的最迟开始时间-----VL(k)

c.活动(Activity)的最早(Early)开始时间:

如果这个活动i是由<事件j,事件k>之间的,那么容易知道活动i最早的开始时间和时间j最早的发生时间是一样的

AE(i) = VE(j);

d.活动(Activity)的最迟(Late)发生时间:是指在不会引起工期延误的前提下,活动ai允许的最迟开始时间.

如果这个活动i是由<事件j,事件k>之间的,为不推迟工期,活动i的最迟开始时间AL(i)应该是i的最迟完成时间VL(k)减去i的持续时间,即AL(i) = VL(k) - ACT[j][k];(ACT --activity time)

e.松弛时间(Share time):就是这个活动最迟开始时间和最早开始时间的差:AL[i]-AE[i]

松弛时间为0,那么这个活动为关键活动;

(上面的东东有一个大前提:一个活动开始,那么它之前的活动必须全部完成)

f.逆拓扑序列:拓扑序列反过来;

g.怎么样求AE,VE,AL,VL?

基于上面的定义,我们可以用式子简单表示:

VE:从VE[1]=0开始向前递推,VE[i]=max{VE[j]+ACT},其中是集合{指向Vi的所有边}中的一个元素;

VL:从VL[n]=VE[n]开始反向递推,VL[i]=min{VL[j]-ACT},期中是集合{从Vi发出的所有边}中的一个元素;

AE:活动k用表示,AE[k]=VE[i];

AL:活动k用表示,AL[k]=AL[j]-ACT
h.算法

1.建立邻接表;

2.从源点出发,令VE[1]=0,按照拓扑顺序求解VE[i](判断有没有环);

3.从结束点出发,VL[n]=VE[n],按照逆拓扑序列求解VL[i];

4.求解AE[i],AL[i];

5.如果是关键活动,输出;

hint:以上全部是自己YY的,不是按照什么专业术语严格证明的,大家看懂个大概,严格的定义和求解还是看书吧!

象征性的再贴一段代码~

//Topsort And AOE   
#include    
#include   
#include   
#include   
using namespace std;  
struct link  
{  
    int v;//事件编号   
    int count;//活动的编号   
    int weight;//活动的时间   
    link * next;  
};  
struct node  
{  
    int indegree;//入度   
    char element;//事件   
    struct link* firstedge;  
};//头结点   
struct Adgraph  
{  
    int n,e;  
    struct node Ad[101];  
};//邻接表   
void Create_AOE(struct Adgraph* G)  
{  
    int k,i,j,t;  
    cin >> G->n >> G->e;//节点和边   
    for (k=1; k<=G->n; k++)  
    {  
        cin >> G->Ad[k].element;  
        G->Ad[k].firstedge=NULL;  
        G->Ad[k].indegree=0;  
    }//头结点的初始化   
    for(k=1; k<=G->e; k++)  
    {  
        printf("输入两个顶点(事件编号),边的权值(活动时间)\n");  
        cin >> j >> i >> t;  
        G->Ad[i].indegree++;  
        link* p=new link;  
        p->v=i;  
        p->weight=t;  
        p->next=G->Ad[j].firstedge;  
        G->Ad[j].firstedge=p;//在表头插入   
    }  
    printf("AOE网络构建完成\n-----人家是华丽丽的分割线-----\n打印邻接表:\n");  
    for(i=1; i<=G->n; i++)  
    {  
        cout << G->Ad[i].element;  
        link *m=G->Ad[i].firstedge;  
        while(m!=NULL)  
        {  
            printf("->%c,%d",G->Ad[m->v].element,m->weight);  
            m=m->next;  
        }  
        printf("->^\n");  
    }//邻接表打印   
    printf("\n");  
}  
void Criticalpath(Adgraph* G)//G为带权值的邻接表   
{  
    queue Q;  
    stack S;  
    int i,j,k,count=0,ve[101],vl[101],ae,al;  
    //时间的最早发生时间和最晚发生时间,活动的最早发生时间和最晚发生时间   
    //m用来计数,判断是否有回路   
    for(i=1; i<=G->n; i++)ve[i]=0; //首先每个事件的最早发生时间都为0   
    for(i=1; i<=G->n; i++)  
        if(G->Ad[i].indegree==0) Q.push(i);  
    //将入度为0的顶点入队   
    printf("Topsort:");  
    while(!Q.empty())  
    {  
        j=Q.front();  
        Q.pop();  
        count++;  
        cout << G->Ad[j].element;  
        S.push(j);//把正序的拓扑序下标列入栈   
        link *p=G->Ad[j].firstedge;  
        while(p!=NULL)  
        {  
            k=p->v;  
            G->Ad[k].indegree--;  
            if(ve[j] + p->weight > ve[k])  
                ve[k] = ve[j] + p->weight;  
            if(G->Ad[k].indegree==0) Q.push(k) ;  
            p=p->next;  
        }  
    }//用topsort求最早的发生时间   
    printf("\n");  
    if(countn)  
    {  
        printf("有环路!\n");  
        return;  
    }  
    for(i=1; i<=G->n; i++) //为各事件v(i)的最迟发生时间vl[i]置初值   
        vl[i]=ve[G->n];  
    printf("Opp_Topsort:");  
    while(!S.empty())//按拓扑序列的逆序取顶点   
    {  
        j=S.top();  
        S.pop();//出栈   
        cout << G->Ad[j].element;  
        link *p=G->Ad[j].firstedge;  
        while(p!=NULL)  
        {  
            k=p->v;  
            if((vl[k] - p->weight)weight;  //修改vl[j]   
            p=p->next;  
        }  
    }  
    printf("\nActivityEnventB>      AE     AL    Share time  Is Criticalpath?:\n");  
    for(j=1; j<=G->n; j++) //扫描顶点表   
    {  
        link *p=G->Ad[j].firstedge;  
        while(p!=NULL)  
        {  
            k=p->v;  
            ae=ve[j];  
            al=vl[k]-p->weight;  
            printf("<事件%c,事件%c>\t\t\t%d\t%d\t%d    \t",G->Ad[j].element,G->Ad[k].element,ae,al,al-ae);  
            if(al==ae)//关键活动   
                printf("Yes");  
                else printf("No");  
            printf("\n");  
            p=p->next;  
        }  
    }  
}  
int main()  
{  
    struct Adgraph G;  
    Create_AOE(&G);  
    Criticalpath(&G);  
    return 0;  
}  


 

你可能感兴趣的:(Data,structure,and,Algorithm)