zoukankan      html  css  js  c++  java
  • 关键路径(拓扑排序)

    一.先给出几个概念:

    AOE-网:在带权有向图中顶点表示事物,有向边表示活动,权表示活动持续的时间,则此有向图称为边表示活动的网络。(Activity On Edge Network)

    (表示实际工程的AOE-网应该是无环的,且存在唯一入度为0的起始顶点(始点),以及唯一出度为0的完成顶点(终点)。)

    利用AOE-网进行工程安排的估算,可以实现:

    1.研究完成工程至少需要多少时间。

    2.为缩短时间,应加快哪些活动的速度等问题。
    用e(i)表示活动ai的最早开始时间,l(i)表示ai的最迟开始时间,e(i)=l(i)的活动称为关键活动,由这些关键活动连接起来的路径称为关键路径。

    二.原理:

    问题在于如何求e(i),l(i)。

    若Ve(i):事件i的最早发生时间;Vl(i):事件最迟发生时间。(注意区分,活动表示边,时间表示顶点,e(i),l(i)是对边而言,Ve(i),Vl(i)是对顶点而言。

    故可将对e(i),l(i)的计算转化成对Ve,Vl的计算,而对Ve,Vl的计算可以通过拓扑排序的过程来实现。

    e(i)=Ve(j)          

    l(i)=Vl(k)-dut(<j,K>)

    三.具体实现:

    1.这里对图的存储使用邻接表,因为临界矩阵很难实现,他无法体现边的性质。

    但需要对邻接表做些改动:在表头结点中加一项index,用于存放入度,并同时作为拓扑栈和逆拓扑栈。当然也可以不这样做,但会耗费一定的空间。代码如下:

    #include<cstdio>
    #include<cstdlib>
    #define LEN sizeof(tablenode)
    using namespace std;
    
    typedef struct node1{                   //定义表结点tablenode
    	int adjvex;                         //顶点
    	int dut;                            //边的权值
    	struct node1* next;                 //指向下一个表结点的指针
    }tablenode;
    
    typedef struct node2{                   //定义表头结点header
    	int index;                          //用作存放入度和拓扑栈、逆拓扑栈
     	int vertex;                         //顶点
    	struct node1* out;                  //指向该顶点的出边
    }header;
    
    

    2.建立邻接表:

    我这里图的顶点即采用0..n-1(n为顶点个数),但输入边的时候是输的1..n,所以建立邻接表需要减一。另外,在建立的同时需要计算出每个顶点的入度,为了后面拓扑排序时方便。代码如下:

    void buildAdjlist(header (&A)[101],int n,int e){         //建立邻接表
    	for(int i=0;i<n;i++){               //初始化邻接表
    		A[i].index=0;                   //入度初始化为0
    		A[i].vertex=i;                  //顶点初始化为i
    		A[i].out=NULL;                  //指向出边的指针初始化为NULL
    	}
    	for(int i=0;i<e;i++){
    		int start,end,dut;
    		scanf("%d%d%d",&start,&end,&dut);  //输入边的始点,终点,权值
    		start--;
    		end--;                          //将输入的点转换成数组下标
    		tablenode* P=(tablenode*)malloc(LEN);
    		P->adjvex=end;
    		P->dut=dut;
    		P->next=A[start].out;
    		A[start].out=P;                 //在A[start]的表中插入P
    		A[end].index++;	                //A[end]的入度加一
    	}
    }
    
    

    3.求关键路径:

    这里大概有3步:根据拓扑排序计算Ve,根据拓扑排序的序列反向进行逆拓扑排序计算Vl,输出关键路径。

    具体的实现看如下的代码:

    void CPM(header (&A)[101],int n){
    	int ftop=-1,btop=-1;                //拓扑栈,逆拓扑栈栈顶指针初始化为-1
    	int m=0;                            //拓扑排序计数器
    	int Ve[101],Vl[101];                //记录顶点的最快发生时间,最迟发生时间
    	for(int i=0;i<n;i++){
    		if(A[i].index==0){
    			A[i].index=ftop;            //将入度为0的顶点入栈
    			ftop=i;
    		}	
    		Ve[i]=0; 	                    //最快发生时间为0
    		
    	}
    	while(ftop!=-1){                    //拓扑排序
    		m++;
    		int p=ftop;
    		ftop=A[ftop].index;             //拓扑栈出栈
    		A[p].index=btop;
    		btop=p;							//逆拓扑栈入栈
    		tablenode* k=A[p].out;
    		while(k!=NULL){
    			int x=k->adjvex;
    			A[x].index--;               //入度减一
    			if(A[x].index==0){
    				A[x].index=ftop;
    				ftop=x;                 //若入度为0则入栈
    			}
    			if(Ve[x]<Ve[p]+k->dut)
    				Ve[x]=Ve[p]+k->dut;     //若通过始点p能增大终点x的Ve则增大
    			k=k->next;			
    		}
    	}
    	if(m<n){                            //若m<n,则该图有环
    		printf("Error:The network has a cycle!");
    		return;
    	}
    	int endnode=btop; 
    	Vl[endnode]=Ve[endnode];            //最后一个顶点的Ve=Vl
    	while(btop!=-1){                    //逆拓扑排序
    		int p=btop;
    		btop=A[btop].index;             //逆拓扑栈出栈
    		Vl[p]=Vl[endnode];              //因为是取最小值,故Vl均初始化为最大值
    		tablenode* k=A[p].out;
    		while(k!=NULL){
    			int x=k->adjvex;
    			if(Vl[p]>Vl[x]-k->dut)
    				Vl[p]=Vl[x]-k->dut;     //若通过终点k能减小始点p的Vl则减小
    			k=k->next;
    		}
    	}
    	printf("Critical Pathes are as follows:
    ");
    	for(int i=0;i<n;i++){               //输出关键路径
    		tablenode* k=A[i].out;
    		while(k!=NULL){
    			int x=k->adjvex;
    			if(Ve[i]==Vl[x]-k->dut)
    				printf("<%d,%d>
    ",i+1,x+1);
    			k=k->next;
    		}
    	}
    }
    
    

    4.主函数如下:

    int main(){
    	header A[101];	                    //定义邻接表
    	int n,e;                            //顶点数n,边的条数e
    	printf("Please enter the number of vertex and side:
    ");
    	scanf("%d%d",&n,&e);                //输入n和e
    	buildAdjlist(A,n,e);                //建立邻接表
     	printf("
    ");
    	CPM(A,n);
    	return 0;
    }
  • 相关阅读:
    Bufferedreader和BufferedWriter(带缓存的输入输出)
    FileReader和FileWriter
    Map接口的类实现
    Map接口
    Set集合
    List接口的实现类
    获取文本框或密码框中的内容
    ADTS (zz)
    初级美语 L003:My Family 解析
    初级美语 L001:Self Introduction 解析
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/10326082.html
Copyright © 2011-2022 走看看