1. 程式人生 > >ISAP網絡流算法

ISAP網絡流算法

由於 其中 分組 actually 時也 其余 return imp ota

  ISAP全稱Improved Shortest Augmenting Path,意指在SAP算法進行優化。SAP即Edmonds-Karp算法,其具體思路是通過不斷向殘存網絡推送流量來計算整個網絡的最大流。閱讀本文要求掌握網絡流的基礎概念,不懂的出門左拐算法導論。ISAP的時間復雜度與EK算法一致,而EK算法的時間復雜度為min(O(E|f|),O(VE^2)),其中O(E|f|)部分是因為其是在FORD-FULKERSON算法上的改進。EK算法在FF算法的基礎上將隨意取增廣路徑替換為取最短增廣路徑,而ISAP在EK算法的基礎上剔除了除了第一次外的後續廣度優先搜索尋找最短路徑的部分。下面對ISAP算法進行說明。

  先說明一些名詞,稱殘存網絡中容量非0的邊為有效邊。

  我們首先在EK算法的基礎上,為每個頂點添加新的屬性d,表示其到匯點t的最短路徑(路徑只能包含有效邊),這個最短路徑是基於殘存網絡的,而非原圖,因此d屬性會隨著流的推送導致殘存網絡的變更而變更。顯然要維護這樣一個屬性d,每次在推送流後,都需要基於新形成的殘存網絡重新執行廣度優先搜索算法,以計算每個頂點正確的d屬性,這樣不就與EK算法完全一樣了嗎?是的,因此為了避免每次流推送都必須重新計算最短路徑,我們需要修改d的定義,d表示頂點到t的距離的某個下界。為了說明這樣定義之後就不用再每次重新執行廣度優先搜索算法計算d,只需要說明每次向殘存網絡推送流,只會導致每個頂點到t的距離非嚴格遞增:

  證明:假設我們向圖G沿最短路徑推送流,並形成新圖G‘。我們記R(a,b)表示原圖中從點a到點b的最短距離,記R‘(a,b)表示在新圖(殘存網絡)中a到b的最短距離。我們所要證明的就是對於任意點x都有R(x,t)<=R‘(x,t)。假設存在點x,滿足R(x,t)>R‘(x,t),顯然x到t的最短路徑之中必然包含由於流推送而新增的邊,假設(v,u)是圖G‘中x到t中距離點x最近的新增邊,而(u,v)是處於G中最短增廣路徑上。這樣我們就可以把G‘中的x到t的最短路徑寫作x,...,v,u,...,t,顯然R‘(x,v)=R(x,v),因為其中不含新增邊和刪除邊(即與G上的最短增廣路徑無交集)。由於R‘(x,t)<R(x,t)可得R‘(v,t)<R(v,t),從而有R‘(u,t)<R(u,t),因此由前面的說明可知存在增廣路徑的邊(q,w),使得(w,q)存在於R‘(u,t)代表的最短路徑中。利用同樣的思路證明下去我們可以得到無窮多的增廣路徑上的邊出現在了R‘(x,t)對應的路徑之中,而由於一條最短路徑是不可能包含重復邊,且所有不同邊的總數最多只有2*|E|條(正向邊以及逆向邊),因此這與R‘(x,t)是最短路徑這一性質相悖,故假設不成立,因此R‘(x,t)>=R(x,t)。

  繼續說明流程的執行。由於d是描述一個頂點距離t的下界,因此當一個頂點的屬性d超過|V|時,這個頂點將永遠無法推送流到t(最短路徑不含相同頂點的簡單應用)。而我們可以以源點s的屬性d是否超過|V|來作為結束推送的條件。具體流程是我們循環推送,每次都從s開始向其余所有與s相鄰且d屬性為s.d-1的頂點推送無窮的流。我們期望這樣的流推送成功當且僅當增廣路徑上的頂點序列的d屬性為s.d,s.d-1,...,1,0,否則推送就不應該成功。顯然在推送成功的情況下,由於d是距離下界,因此推送路徑一定是最短的增廣路徑。而所謂的推送失敗是指抵達一個頂點x(非t),所有鄰接的可抵達頂點(指存在有效邊連接)的距離都不等於x-1,這意味著附近的所有頂點到t的距離都由於前面對流的推送而增大了,因此我們也應該對x的距離進行增大(因為一個頂點距離t的最短距離應該是其與可以通過有效邊連接的所有頂點到t的最短距離的最小值+1),由於周邊的所有頂點的d屬性均>=x.d,因此我們將x.d賦值為x.d+1也是合適的,不會違背我們對d的定義,在我們修改了d的值之後,顯然這與我們期望的可推送的增廣路徑已經不相符了,因為d屬性為x.d+1的頂點出現了兩次,因此我們宣告失敗回到之前的頂點。而當我們推送成功且x是方才推送的增廣路徑上的某個頂點,且此時從前面頂點傳來的流還足夠支持下次推送,那麽我們可以繼續尋找其滿足d屬性為x.d-1的後繼頂點,繼續推送(這也是ISAP的優化之一)。

  先說明程序會正確停止。由於推送成功就意味著存在增廣路徑(而且是符合嚴苛條件的最短增廣路徑),此時顯然還可以繼續通過向增廣路徑推送流來獲得最大流。當我們發現不存在增廣路徑時,即s向周邊推送失敗,我們會不斷增大s.d屬性,而當s.d>|V|時,我們就結束推送,此時程序正確結束。

  時間復雜度與EK基本一致,只是將其中原本每次成功推送後執行廣度優先搜索去掉,取而代之的是增加了推送失敗情況下對頂點d屬性的增大。由於一個頂點d屬性不會超過|V|,因此我們可以保證每個頂點d屬性最多增大|V|次,而總共有|V|個頂點,因此失敗處理次數不會超過|V|^2。同時由於我們每次推送成功時也會遍歷大量的不同邊和不同頂點,這個流程在最糟糕的情況下的時間復雜度為O(|V|+|E|),與廣度優先搜索算法一致,因此算法上界應該為O(VE^2+V^2)=O(VE^2),與EK算法一致。

  ISAP有一個通用的GAP優化就是我們將擁有不同d屬性值的頂點進行分組,以d屬性值作為編號。當我們修正一個頂點p的d屬性值(推送失敗時),我們可以通過判斷p.d這一組元素是否只有一個,當只有一個時,意味著我們再修改p.d為p.d+1後,p.d這一組將會為空,這是一個好消息,因為後面所有從s發出的推送請求都會由於沒有p.d組的頂點可以接受請求而失敗,因此我們可以斷定此時殘存網絡中的流即是最大流,我們可以通過提前將s.d修改為|V|來跳過後面一系列的失敗處理,從而優化性能。

  下面給出代碼:

  

bfs(t) //從t開始進行廣度優先搜索,如果頂點v到t無路徑,則v.d=-1
  for v in V
    v.d=-1    t.d
= 0 que = empty-queue que.enque(t) while(!que.isEmpty()) head = que.deque() group[head.d]+=1 for edge in head.edgeList if(edge.isNegative)//只允許沿著有效邊的反向邊移動 if(edge.dst.d == -1) edge.dst.d = head.d + 1 que.enque(edge.dst) sendFlow(node, flowLimit) //通過頂點node向周圍最多推送flowLimit單位的流,如果推送量為0,則表示推送失敗 total = 0 for edge in node.edgeList if(edge.capacity > edge.flow && edge.dst.d == node.d - 1) //發現符合條件的有效邊 actually = sendFlow(edge.dst, min(flowLimit, edge.capacity - edge.flow) flowLimit -= actually total += actually
       if(flowLimit == 0)
         break
if(total == 0) //如果發送失敗 group[node.d]-=1 if(group[node.d] == 0) //發現斷層 s.d = |V| node.d+=1 group[node.d]+=1 return total isap(s, t) bfs(t) if(s.d == -1)//如果從s到t不存在路徑 return 0 sum = 0 while(s.d < |V|) sum += sendFlow(s, INF)
   return sum

ISAP網絡流算法