1. 程式人生 > >關鍵路徑在演算法題中的應用

關鍵路徑在演算法題中的應用

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];
}