zoukankan      html  css  js  c++  java
  • 关键路径 图论 ——数据结构课程

    AOE网(activity on edge network):在带权有向图中,用顶点表示事件,用有向边表示活动。边权表示活动的持续时间。

    源点,终点:AOE图中有唯一的源点和终点,表示活动的开始与终结。

    关键路径:所有的活动都完成时整个工程所花费的时间,其实也就是源点到终点的最长路径,该最长路径被称为关键路径(可能有多条),关键路径上的活动被称为关键活动。

    事件的最早发生时间:ve[k]:根据AOE网的性质,只有进入Vk的所有活动<Vj, Vk>都结束,Vk代表的事件才能发生,而活动<Vj, Vk>的最早结束时间为ve[j]+len<Vj, Vk>。所以,计算Vk的最早发生时间的方法为:ve[k] = max(ve[j] + len<Vj, Vk>)


    事件的最迟发生时间:vl[k]:vl[k]是指在不推迟整个工期的前提下,事件Vk允许的最迟发生时间。根据AOE网的性质,只有顶点Vk代表的事件发生,从Vk出发的活动<Vk, Vj>才能开始,而活动<Vk, Vj>的最晚开始时间为min (vl[j] - len<Vk, Vj>)


    活动的最早发生时间:ee[i]:ai由有向边<Vk, Vj>,根据AOE网的性质,只有顶点Vk代表的事件发生,活动ai才能开始,即活动ai的最早开始时间等于事件Vk的最早开始时间。


    活动的最迟发生时间:el[i]el[i]是指在不推迟真个工期的前提下,活动ai必须开始的最晚时间。若活动ai由有向边<Vk, Vj>表示,则ai的最晚开始时间要保证事件vj的最迟发生时间不拖后。

    具体思路为:先对AOE图进行拓扑排序,排序后顺序更新节点最早发生时间找到最长路径,然后逆序更新节点最迟发生时间。

          最后计算边的最早发生时间和最晚发生时间,其差为0的边为关键路径(因为只是最长路径上的边才会出现0)。

          (在邻接表储存过程中要储存逆邻接表,由终点向源点更新最迟发生时间)

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<math.h>
    #include<queue>
    using namespace std;
    const int MaxVnum=100;   //顶点数最大值
    int indegree[MaxVnum];   //入度数组
    int ve[MaxVnum];         //事件vi的最早发生时间
    int vl[MaxVnum];         //事件vi的最迟发生时间
    int St[MaxVnum] ;
    int top=-1;
    typedef string VexType;     //顶点的数据类型为字符型
    typedef struct AdjNode {    //定义邻接点类型
        int v;                 //邻接点下标
        int weight;            //权值
        struct AdjNode *next;  //指向下一个邻接点指针
    } AdjNode;
    typedef struct VexNode { //定义顶点类型
        VexType data;           //VexType为顶点的数据类型,根据需要定义
        AdjNode *first;         //指向第一个邻接点指针
    } VexNode;
    typedef struct { //包含邻接表和逆邻接表的一张图
        VexNode Vex[MaxVnum];          //定义邻接表
        VexNode converse_Vex[MaxVnum]; //定义逆邻接表
        int vexnum, edgenum;           //顶点数,边数
    } ALGragh;
    //以下为需要用到的函数
    int locatevex(ALGragh G, VexType x)            ;//寻找名为 X的节点
    void insertedge(ALGragh &G, int i, int j, int w) ;//插入一条边
    void CreateALGraph(ALGragh &G)                ; //通过输入数据,创建有向图的邻接表和逆邻接表
    bool TopologicalSort(ALGragh G, int topo[])    ;//拓扑排序
    void FindInDegree(ALGragh G)                ;//求出各顶点的入度存入数组indegree中
    bool CriticalPath(ALGragh G,int topo[])     ;//G为邻接表存储的有向网,输出G的各项关键活动
    int main() {
        ALGragh G;
        int *topo=new int[G.vexnum];
        CreateALGraph(G);     //创建有向图的邻接表和逆邻接表
        int *topo=new int[G.vexnum];    
        //printg(G);            //输出邻接表和逆邻接表                 尚未定义
        CriticalPath(G,topo);
        return 0;
    }
    /*
    4 5
    0 1 2 3
    0 1 4
    0 2 3
    1 2 3
    1 3 6
    2 3 4
    */ 
    
    
    
    
    int locatevex(ALGragh G, VexType x) { //寻找节点
        for(int i=0; i<G.vexnum; i++) //查找顶点信息的下标
            if(x==G.Vex[i].data)
                return i;
        return -1;      //没找到
    }
    void insertedge(ALGragh &G, int i, int j, int w) { //插入一条边
        AdjNode *s1,*s2;
        //创建邻接表结点
        s1=new AdjNode;
        s1->v=j;
        s1->weight=w;
        s1->next=G.Vex[i].first;
        G.Vex[i].first=s1;
        //创建逆邻接表结点
        s2=new AdjNode;
        s2->v=i;
        s2->weight=w;
        s2->next=G.converse_Vex[j].first;
        G.converse_Vex[j].first=s2;
    }
    void CreateALGraph(ALGragh &G) { //通过输入数据,创建有向图的邻接表和逆邻接表
        int i,j,w;
        VexType u,v;
        cout<<"请输入顶点数和边数:"<<endl;
        cin>>G.vexnum>>G.edgenum;
        cout<<"请输入顶点信息:"<<endl;
        for(i=0; i<G.vexnum; i++) { //输入顶点信息,存入顶点信息数组
            cin>>G.Vex[i].data;
            G.converse_Vex[i].data=G.Vex[i].data;
            G.Vex[i].first=NULL;
            G.converse_Vex[i].first=NULL;
        }
        cout<<"请依次输入每条边的两个顶点及权值u,v,w"<<endl;
        while(G.edgenum--){
            cin>>u>>v>>w;
            i=locatevex(G,u);   //查找顶点u的存储下标
            j=locatevex(G,v);   //查找顶点v的存储下标
            if(i!=-1&&j!=-1)
                insertedge(G,i,j,w);
            else {
                cout<<"输入顶点信息错!请重新输入!"<<endl;
                G.edgenum++;    //本次输入不记入
            }
        }
    }
    bool TopologicalSort(ALGragh G, int topo[]) { //拓扑排序
        //拓扑排序。有向图G采用邻接表存储结构
        //若G无回路,则生成G的一个拓扑序列topo[]并返回true,否则false
        int i, count;
        FindInDegree(G);  //求出各顶点的入度存入数组indegree[]中
        for(i=0; i<G.vexnum; i++)
            if(!indegree[i]) {//入度为0顶点进栈
                top++;
                St[top]=i;
            } //St.push(i);
        count=0;            //对输出顶点计数,初始为0
        while(top!=-1) { //栈S非空
            i=St[top];    //取栈顶顶点i
            top--;      //栈顶顶点i出栈
            topo[count]=i;    //将i保存在拓扑序列数组topo中
            count++;          //对输出顶点计数
    
            AdjNode *p=G.Vex[i].first;  //p指向i的第一个邻接点
            while(p) {              //i的所有邻接点入度减1
                int k=p->v;             //k为i的邻接点
                --indegree[k];       //i的每个邻接点的入度减1
                if(indegree[k]==0) { //若入度减为0,则顶点入栈
                    top++;
                    St[top]=k;
                }
                p=p->next;      //p指向顶点i下一个邻接结点
            }
        }
        if(count<G.vexnum)   //该有向图有回路
            return false;
        else
            return true;
    }
    void FindInDegree(ALGragh G) { //求出各顶点的入度存入数组indegree中
        int i,count;
        for(i=0; i<G.vexnum; i++) {
            count=0;
            AdjNode *p=G.converse_Vex[i].first;
            if(p) {
                while(p) {
                    p=p->next;
                    count++;
                }
            }
            indegree[i]=count;
        }
        cout<<"入度数组为:"<<endl;
        for(int i=0; i<G.vexnum; i++) //输出入度数组
            cout<<indegree[i]<<"\t";
        cout<<endl<<endl;
    }
    bool CriticalPath(ALGragh G,int topo[]) { //G为邻接表存储的有向网,输出G的各项关键活动
        int n,i,k,j,e,l;
        if(TopologicalSort(G,topo)) {
            cout<<"拓扑序列为:"<<endl;
            for(int i=0; i<G.vexnum; i++) //输出拓扑序列
                cout<<topo[i]<<"\t";
            cout<<endl<<endl;
        } else
            cout<<"该图有环,无拓扑序列!"<<endl;
        n=G.vexnum;          //n为顶点个数
        for(i=0; i<n; i++)   //给每个事件的最早发生时间置初值0
            ve[i]=0;
        //按拓扑次序求每个事件的最早发生时间
        for(i=0; i<n; i++) {
            k=topo[i];                    //取得拓扑序列中的顶点序号k
    
            for(i=0; i<n; i++) {
                k=topo[i];                    //取得拓扑序列中的顶点序号k
                AdjNode *p=G.Vex[k].first;    //p指向k的第一个邻接顶点
                while(p!=NULL) {
                    //依次更新k的所有邻接顶点的最早发生时间
                    j=p->v;                   //j为邻接顶点的序号
                    if(ve[j]<ve[k]+p->weight)   //更新顶点j的最早发生时间ve[j]
                        ve[j]=ve[k]+p->weight;
                    p=p->next;                //p指向k的下一个邻接顶点
                }
            }
            for(i=0; i<n; i++)        //给每个事件的最迟发生时间置初值ve[n-1]
                vl[i]=ve[n-1];
            //按逆拓扑次序求每个事件的最迟发生时间
            for(i=n-1; i>=0; i--) {
                k=topo[i];                    //取得逆拓扑序列中的顶点序号k
                AdjNode *p=G.Vex[k].first;    //p指向k的第一个邻接顶点
                while(p!=NULL) {
                    //依次更新k的所有邻接顶点的最早发生时间
                    j=p->v;                   //j为邻接顶点的序号
                    if(ve[j]<ve[k]+p->weight)   //更新顶点j的最早发生时间ve[j]
                        ve[j]=ve[k]+p->weight;
                    p=p->next;                //p指向k的下一个邻接顶点
                }
            }
            for(i=0; i<n; i++)        //给每个事件的最迟发生时间置初值ve[n-1]
                vl[i]=ve[n-1];
            //按逆拓扑次序求每个事件的最迟发生时间
    
            for(i=n-1; i>=0; i--) {
                k=topo[i];                    //取得逆拓扑序列中的顶点序号k
                AdjNode *p=G.Vex[k].first;    //p指向k的第一个邻接顶点
                while(p!=NULL) {
                    //根据k的邻接点,更新k的最迟发生时间
                    j=p->v;                    //j为邻接顶点的序号
                    if(vl[k]>vl[j]-p->weight)   //更新顶点k的最迟发生时间vl[k]
                        vl[k]=vl[j]-p->weight;
                    p=p->next;                //p指向k的下一个邻接顶点
                }
            }
            cout<<"事件的最早发生时间和最迟发生时间:"<<endl;
            for(i=0; i<n; i++)
                cout<<ve[i]<<"\t"<<vl[i]<<endl;
    
            //判断每一活动是否为关键活动
            cout<<"关键活动路径权值之和为:"<<vl[n-1]<<endl;
            cout<<endl;
            cout<<"关键活动路径为:";
            for(i=0; i<n; i++) {   //每次循环针对vi为活动开始点的所有活动
                AdjNode *p=G.Vex[i].first;    //p指向i的第一个邻接顶点
                while(p!=NULL) {
                    j=p->v;                   //j为i的邻接顶点的序号
                    e=ve[i];              //计算活动<vi, vj>的最早开始时间e
                    l=vl[j]-p->weight;   //计算活动<vi, vj>的最迟开始时间l
                    if(e==l)              //若为关键活动,则输出<vi, vj>
                        cout<<"<"<<G.Vex[i].data<<","<<G.Vex[j].data<<">    ";
                    p=p->next;            //p指向i的下一个邻接顶点
                }
            }
            return true;
        }
    }
    寻找关键路径
  • 相关阅读:
    【题解】LOJ #6488 数表【FWT】
    【题解】[Comet OJ Contest #11 F] arewell【子集卷积】
    【CF757F】 Team Rocket Rises Again 【支配树】
    支配树学习笔记
    JS模拟实现题目(new debounce throwee 等)
    React生命周期
    js转义符
    CSS3中的transform转换属性
    animation动画
    flex
  • 原文地址:https://www.cnblogs.com/CLGYPYJ/p/15629154.html
Copyright © 2011-2022 走看看