1. 程式人生 > 其它 >關鍵路徑 圖論 ——資料結構課程

關鍵路徑 圖論 ——資料結構課程

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; } }
尋找關鍵路徑