Dinic算法詳解及實現

預備知識:

殘留網絡:設有容量網絡G(V,E)及其上的網絡流f,G關於f的殘留網絡即爲G(V',E'),其中G’的頂點集V'和G的頂點集V相同,即V'=V,對於G中任何一條弧<u,v>,若是f(u,v)<c(u,v),那麼在G'中有一條弧<u,v>∈E',其容量爲c'(u,v)=c(u,v)-f(u,v),若是f(u,v)>0,則在G'中有一條弧<v,u>∈E',其容量爲c’(v,u)=f(u,v).node

從殘留網絡的定義來看,原容量網絡中的每條弧在殘留網絡中都化爲一條或者兩條弧。在殘留網絡中,從源點到匯點的任意一條簡單路徑都對應一條增光路,路徑上每條弧容量的最小值即爲可以一次增廣的最大流量。ios

頂點的層次:在殘留網絡中,把從源點到頂點u的最短路徑長度,稱爲頂點u的層次。源點 Vs的層次爲0.例以下圖就是一個分層的過程。算法

 

注意:數組

(1)對殘留網路進行分層後,弧可能有3種可能的狀況。網絡

一、從第i層頂點指向第i+1層頂點。優化

二、從第i層頂點指向第i層頂點。spa

三、從第i層頂點指向第j層頂點(j < i)。code

(2)不存在從第i層頂點指向第i+k層頂點的弧(k>=2)。blog

(3)並不是全部的網絡都能分層。it

 

二、最短路增廣路徑的算法思想

最短增廣路的算法思想是:每次在層次網絡中找一條含弧數最少的增廣路進行增廣。最短增廣路算法的具體步驟以下:

(1)初始化容量網絡和網絡流。

(2)構造殘留網絡和層次網絡,若匯點不在層次網絡中,則算法結束。

(3)在層次網絡中不斷用BFS增廣,直到層次網絡中沒有增廣路爲止;每次增廣完畢,在層次網絡中要去掉因改進流量而致使飽和的弧。

(4)轉步驟(2)。

在最短增廣路算法中,第(2)、(3)步被循環執行,將執行(2)、(3)步的一次循環稱爲一個階段。在每一個階段中,首先根據殘留網絡創建層次網絡,而後不斷用BFS在層次網絡中增廣,直到出現阻塞流。注意每次增廣後,在層次網絡中要去掉因改進流量而飽和的弧。該階段的增廣完畢後,進入下一階段。這樣的不斷重複,直到匯點不在層次網絡中爲止。匯點不在層次網絡中意味着在殘留網絡中不存在一條從源點到匯點的路徑,即沒有增廣路。

在程序實現的時候,並不須要真正「構造」層次網絡,只須要對每一個頂點標記層次,增廣的時候,判斷邊是否知足level(v) = level(u)+1這一約束條件便可。

三、最短增廣路算法複雜度分析

最短增廣路的複雜度包括創建層次網絡和尋找增廣路兩部分。

在最短增廣路中,最多創建n個層次網絡,每一個層次網絡用BFS一次遍歷便可獲得。一次BFS的複雜度爲O(m),因此建層次圖的總複雜度爲O(n*m)。

每增廣一次,層次網絡中一定有一條邊會被刪除。層次網絡中最多有m條邊,因此認爲最多能夠增廣m次。在最短增廣路算法中,用BFS來增廣,一次增廣的複雜度爲O(n+m),其中O(m)爲BFS的花費,O(n)爲修改流量的花費。因此在每一階段尋找增廣路的複雜度爲O(m*(m+n)) = O(m*m)。所以n個階段尋找增廣路的算法總複雜度爲O(n*m*m)。

二者之和爲O(n*m*m)。

以上介紹最短增廣路算法只是爲下面介紹Dinic算法而提供給你們一個鋪墊,有了以上的基礎,接下來咱們來介紹Dinic算法,Dinic實際上是最短增廣路算法的優化版。

 

 

 

連續最短增廣路算法----Dinic算法

一、Dinic算法思路

Dinic算法的思想也是分階段地在層次網絡中增廣。它與最短增廣路算法不一樣之處是:最短增廣路每一個階段執行完一次BFS增廣後,要從新啓動BFS從源點Vs開始尋找另外一條增廣路;而在Dinic算法中,只需一次DFS過程就能夠實現屢次增廣,這是Dinic算法的巧妙之處。Dinic算法具體步驟以下:

(1)初始化容量網絡和網絡流。

(2)構造殘留網絡和層次網絡,若匯點再也不層次網絡中,則算法結束。

(3)在層次網絡中用一次DFS過程進行增廣,DFS執行完畢,該階段的增廣也執行完畢。

(4)轉步驟(2)。

在Dinic的算法步驟中,只有第(3)步與最短增廣路相同。在下面實例中,將會發現DFS過程將會使算法的效率有很是大的提升。

Dinic算法複雜度分析

與最短增廣路算法同樣,Dinic算法最多被分爲n個階段,每一個階段包括建層次網絡和尋找增廣路兩部分,其中創建層次網絡的複雜度還是O(n*m)。

如今來分析DFS過程的總複雜度。在每一階段,將DFS分紅兩部分分析。

(1)修改增廣路的流量並後退的花費。在每一階段,最多增廣m次,每次修改流量的費用爲O(n)。而一次增廣後在增廣路中後退的費用也爲O(n)。因此在每一階段中,修改增廣路以及後退的複雜度爲O(m*n)。

(2)DFS遍歷時的前進與後退。在DFS遍歷時,若是當前路徑的最後一個頂點可以繼續擴展,則必定是沿着第i層的頂點指向第i+1層頂點的邊向匯點前進了一步。由於增廣路經長度最長爲n,因此最壞的狀況下前進n步就會遇到匯點。在前進的過程當中,可能會遇到沒有邊可以沿着繼續前進的狀況,這時將路徑中的最後一個點在層次圖中刪除。

注意到每後退一次一定會刪除一個頂點,因此後退的次數最多爲n次。在每一階段中,後退的複雜度爲O(n)。

假設在最壞的狀況下,全部的點最後均被退了回來,一共共後退了n次,這也就意味着,有n次的前進被「無情」地退了回來,這n次前進操做都沒有起到「尋找增廣路」的做用。除去這n次前進和n次後退,其他的前進都對最後找到增廣路作了貢獻。增廣路最多找到m次。每次最多前進n個點。因此全部前進操做最多爲n+m*n次,複雜度爲O(n*m)。

因而獲得,在每一階段中,DFS遍歷時前進與後退的花費爲O(m*n)。

綜合以上兩點,一次DFS的複雜度爲O(n*m)。所以,Dinic算法的總複雜度即O(m*n*n)。

 

 

下面的實現,有借鑑別人的方法。主要是利用BFS構建層次網絡,這裏用level數組來存儲每一個頂點的層數。

而後利用dfs進行增廣,默認M個節點,第M個節點就是匯點。而後當第M個節點不在分層網絡時,就結束。

 

#include <iostream>
#include <queue>
using namespace std;

const int INF = 0x7fffffff;
int V, E;
int level[205];
int Si, Ei, Ci;

struct Dinic
{
    int c;
    int f;
}edge[205][205];

bool dinic_bfs()      //bfs方法構造層次網絡
{
    queue<int> q;
    memset(level, 0, sizeof(level));
    q.push(1);
    level[1] = 1;
    int u, v;
    while (!q.empty()) {
        u = q.front();
        q.pop();
        for (v = 1; v <= E; v++) {
            if (!level[v] && edge[u][v].c>edge[u][v].f) {
                level[v] = level[u] + 1;
                q.push(v);
            }
        }
    }
    return level[E] != 0;                //question: so it must let the sink node is the Mth?/the way of yj is give the sink node's id
}

int dinic_dfs(int u, int cp) {           //use dfs to augment the flow
    int tmp = cp;
    int v, t;
    if (u == E)
        return cp;
    for (v = 1; v <= E&&tmp; v++) {
        if (level[u] + 1 == level[v]) {
            if (edge[u][v].c>edge[u][v].f) {
                t = dinic_dfs(v, min(tmp, edge[u][v].c - edge[u][v].f));
                edge[u][v].f += t;
                edge[v][u].f -= t;
                tmp -= t;
            }
        }
    }
    return cp - tmp;
}
int dinic() {
    int sum=0, tf=0;
    while (dinic_bfs()) {
        while (tf = dinic_dfs(1, INF))
            sum += tf;
    }
    return sum;
}

int main() {
    while (scanf("%d%d", &V, &E)) {
        memset(edge, 0, sizeof(edge));
        while (V--) {
            scanf("%d%d%d", &Si, &Ei, &Ci);
            edge[Si][Ei].c += Ci;
        }
        int ans = dinic();
        printf("%d\n", ans);
    }
    return 0;
}
相關文章
相關標籤/搜索