關鍵路徑在演算法題中的應用
1、AOV和AOE網
AOV是指的用頂點(Vertex)表示活動,用邊集表示活動間優先順序的有向圖,圖中不會有環。
AOE是指用帶權的邊集(Edge)表示活動,用頂點表示事件的有向圖,邊權表示 完成活動所需要的時間。AOE網表示一個工程的進行過程,它也不應該有環,一般來說它只有一個源點(入度為零)和一個匯入點(初度為零),其實AOV網也可以轉換為AOE網。
AOE網著重解決的問題有:①工程從開始到結束需要多少時間②工程中哪些分工程是影響全域性的。一般來說 第二個問題會轉換到最長路徑。
2、最長路徑
和我們在之前看過的最短路徑不同,最長路徑,一般代表的是那種不能拖延的工程,如何求最長路徑呢?
將所有權值變負,然後用FORD演算法算出最小的那個,然後再變正就是了。
3、關鍵路徑
AOE網實際上屬於有向無環圖,下面給出一個求解有向無環圖中最長路徑的方法
首先設定兩組陣列,e和l,第一個表示最早開始的時間,第二個表示最遲開始的時間,如果e==l,則說明這個活動是不能拖延的 ,即為我們所求的關鍵路徑。然後問題來了,怎麼求e和l呢?
事件v1在經過活動a之後到達V2,然而是否立即到達頂點V2,也存在拖延的可能性,所以會出現最早發生和 最遲發生兩種極端情況,某一個事件最遲發生可以認為下一個事件的最遲開始,設定兩個新的陣列Ve和Vl,分別表示其最早開始時間和最遲開始時間:
①對於一個活動,在上一個事件最早發生時開始則會得到最早開始時間,e=Ve;
②對於一個事件,他的最遲發生時間就是上個活動最遲開始時間加上路的權值,即 vl=l+w;
然後對於我們的難題就轉移到求端點的最早開始和最晚開始時間
假設有k個事件,通過相應的活動到達事件J,假設我們已經得到了每個事件的最早發生時間VE,那麼VJ最早發生時間就是各個事件VE+w中的最大值,因為只有所有事件都發生且經過活動到達之後,J才會被啟用,所以想要獲得VE[J]的正確值,VE[V1]~VE[VK]必須全部都到手,通過拓撲排序就可以保證其前驅端點都訪問完畢了,但是不可能通過J去拓撲他的前驅端點,因此可以在訪問他的前驅端點是,就不斷地更新VE[J]。
if(ve[u]+G[u][i].w>ve[j])
{
ve[j]=ve[u]+G[u][i].w;
}
同理,若獲得VJ,那麼1~K的最晚發生時間也可以確定,這時需要保證他的後繼結點都已經被訪問完bool topol()//判斷是否非環 { queue<int >q; for(int i=0;i<n;i++) { if(inDegree[i]==0)//入度為零 { q.push(i); } } while(!q.empty()) { int u=q.front(); q.pop; topOrder.push(u);//加入序列 for(int j=0;j<G[u].size();j++) { int v=G[u][j].v; inDegree[v]--; if(inDegree[v]==0)//入度為零 { q.push(v); } if(ve[u]+G[u][i].w>ve[j]) { ve[j]=ve[u]+G[u][i].w; } } } if(topOrder.size()!=n) return false; else return true; } int CriticalPath() { memset(ve,0,sizeof(ve)); if(topol()==false) { return -1; } fill(vl,vl+n,ve[n-1]); while(!topOrder.empty()) { int u = topOrder.top; topOrder.pop(); for(int i=0;i<G[u].size();i++) { int v=G[u][i].v; if(ve[v]-G[u][i].w<vl[u])//用u的所有後繼點來更新vl[u] { vl[u]=vl[v]-G[u][i].w; } } } for(int u=0;u<n;u++) { for(int i=0;i<G[u].size();i++) { int v =G[u][i].v,w=G[u][i].w; int e=ve[u],l=vl[v]-w; if(e==l) { printf("%d->%d",u,v); } } } return ve[n-1]; }