1. 程式人生 > >網絡流初步詳解

網絡流初步詳解

its isap算法 競賽 深入 name == 有助於 using 暫時

眾所周知,網絡流是探究網絡上運輸的一種圖論分支。但是大多數人在第一次接觸這個題時都有些畏懼感(比如說我),大佬可以自信跳過..


本文包括:

1.網絡流的概念及基本性質

2.略談 Edmonds-Karp增廣路算法

3.詳談 Dinic 算法

4.網絡流的應用以及ISAP算法引入


1 . 網絡流的概念及基本性質

網絡流是圖論的一種重要分支,我們可以將網絡流初步理解為一種 水道 一樣的網絡。

基本定義: 部分參考《算法競賽進階指南》)

對於一個網絡 \(G = (V , E )\) 為一張有向圖,圖中的每條有向邊 $ ( X,Y)?E $ , 則 $ C(X,Y) = 0 $ 。圖中還有兩個節點 $ S , T $ 十分特殊,我們將其稱為 源點

匯點

我們將 \(f\) 函數稱作網絡的流函數

對於 \((X,Y)∈E , f(X,Y)\) 稱為邊的流量 ,$ C(X,Y) - f(X,Y)$ 稱為邊的剩余流量。

知道大佬們都不想看, 那麽直接一點。

假設有一片有向的水域,有多條有向的河流,河流\(S\) 點出發,最終匯向 $ T$ 點。每條河流有一定的寬度,只允許最多不超過該條河流邊權值 的水流通過。

技術分享圖片

如上圖 ,藍點為源點 , 紅點為匯點。而紫色點紫色邊顯然無用,選擇不流過紫色。

** 默認 S 點的水量有無限多。 **

在初步了解後,按照流函數的定義,一個網絡中的每條邊實際上都有一條反向邊,並且這些反向邊都有一個負的流量

, 這個定義將有助於我們解決之後的回溯問題

基本性質:

1.容量限制

任意一條邊的流量必定小於它的容量(及它的邊權)。

2.斜對稱定理

一條邊\((X,Y)\)\(X\)\(Y\) 的流量必定與其反邊\((Y,X)\)\(Y\)\(X\) 的流量相反。

3.流量守恒定理

除了源點 $ S $ 和 匯點 $ T $外 ,任意節點的 流入總量 都等於 流出總量 。即不會存儲流量 。


2.略談 Edmonds-Karp增廣路算法

學習過匈牙利算法的同學已經了解增廣路的定義,沒有的話建議先 $ A$ 掉P3386 【模板】二分圖匹配
,將有利於理解本文(貌似沒有關聯)。

但是增廣路

此時的定義為:

一條增廣路從源點 \(S\) 到匯點 \(T\) 的路徑上各邊的剩余流量的最小值大於 0

Edmonds-Karp增廣路算法(下列簡稱EK算法)的思路就是對該網絡進行BFS,不斷找出其增廣路,直至將該網絡上的所有增廣路全部找出。

EK算法的正確性顯然,在這裏就不給出詳細證明。(有興趣者可參考《算法競賽進階指南》)。

EK算法具體實現過程如下:

1 . 用BFS在網絡上尋找可行增廣路

2 . 在BFS找到任意一條增廣路時計算出該增廣路上各邊剩余流量最小值 min 。

3 . 最大流 \(maxflow\) 的值增加 min ,如此往復直至BFS找不增廣路。

給出圖 及 圖的最大流 7 作參考

技術分享圖片

代碼暫未上傳。

但是EK算法存在明顯的局限性,及每次更新都要從頭到尾BFS一下,只能解決 \(O(nm^2)\) 的網絡。

於是我們又有下面這個經一步優化的算法。


3.詳談 Dinic 算法

之所以詳談Dinic算法,是因為Dinic算法是代碼較簡單,較容易實現並且效率極高的網絡流算法之一,它對於普通的網絡圖,處理範圍可以達到 $ 10^4 - 10^5 $ 。而相比之下,EK算法僅有 $ 10^3 - 10^4 $的處理範圍。

並且某位大師推演出Dinic算法二分圖中的復雜度僅有 $m \sqrt{n} $ , 所以可以在二分圖中愉快地跑網絡流 啦。上一道例題P1129 [ZJOI2007]矩陣遊戲,熟練後可以切了它。

我們先介紹一下Dinic算法的核心之一:殘量網

殘量網是指在當前網絡中的所有節點以及 ** 剩余容量大於 \(0\) ** 的邊構成的子圖

但是有了殘量網還不夠,要精準判斷殘量網上兩點 \(X,Y\) 的關系,即判斷它們是否有之類神奇的邊,我們還要借助滿足 $d[y] = d[x] + 1 $ 的分層圖

Dinic算法思路:

1 . 開一個隊列, 在殘量網中BFS一次,按照遍歷層數為每個點表上層次 $ d[ x ] $ ,構造分層圖並且判斷能否從源點 \(S\) 到達匯點 \(T\) 。如果失敗則說明殘量網無法到達匯點。

2 . 在構造好的殘量網上DFS尋找增廣路並且,更新反邊,(否則DFS無法後悔)

3 . 不斷重復 1 2 步驟直至構造的殘量網無法到達匯點

實踐是檢驗真理的唯一標準

模板題P3376 【模板】網絡最大流

\(AC\)代碼及講解:

#include<bits/stdc++.h>
using namespace std;

const  int  inf =  0x3f3f3f3 , N = 10000 + 19  ;

int  head[ N*2 ], d[ N ] , to[ N*10*2 ] , w[ N*10*2 ] , next[ N*10*2 ]  ;

int  n , m , s, t , tot = 1 , maxflow = 0 ;//maxflow記錄答案 

inline int read()
{
    int s = 0,w = 1;
    char g = getchar();
    while(g<'0'||g>'9'){if(g=='-')w*=-1;g = getchar();}
    while(g>='0'&&g<='9'){s = s*10+g-'0';g = getchar();}
    return s*w;
}

queue<int> q ;//廣搜時采用隊列

void  add( int  x , int  y , int  z ){
    tot++; to[ tot ] = y , w[ tot ] = z , next[ tot ] = head[ x ] , head[ x ] = tot;
    tot++; to[ tot ] = x , w[ tot ] = 0 , next[ tot ] = head[ y ] , head[ y ] = tot;
}//建立邊和反向邊,註意反向邊的流量初始為 0 

bool  bfs(){//在殘量網絡上構造分層圖
    memset( d , 0 , sizeof(d) ) ; //將之前的分層清0,繼續跑殘量網絡找可行增廣路
    while( q.size() ) q.pop() ; //隊列清 0 ;
    q.push( s ) ; d[ s ] = 1 ; //將源點加入隊列,層數為 1 ,開始廣搜 
    while( q.size() ){
        int  x = q.front() ; q.pop() ;
        for( int  i = head[ x ] ; i ; i = next[ i ])
            if( w[ i ] && !d[ to[i] ] ){//目標邊的流量不為0 且 為被遍歷分層
                q.push( to[ i ] ) ;
                d[ to [ i ] ] = d[ x ] + 1;
                if( to[ i ] == t )return 1 ; //找到一條可行增廣路
            }
    }
    return 0 ;//未找到,不存在增廣路
}

int  dinic( int  x , int  flow ){ //在分層圖上進行增廣
    if( x == t )return flow ;//源點即使匯點,流量不限量
    int  rest = flow , k ;
    for( int  i = head[ x ] ; i && rest ; i = next[ i ] )//當前可流入最大流量不為0 ,
        if( w[ i ] && d[ to [ i ] ] == d[ x ] + 1 ){//目標路徑有剩余流量,且不存在環之類神奇的東西
            k = dinic( to[ i ] , min( rest , w[ i ] ) );//繼續搜
            if( !k )d[ to [ i ] ] = 0 ; //剪枝,如果 k(下一層可流入流量圖為 0 ),cut
            w[ i ] -= k ; 
            w[ i ^ 1 ] += k ;//占用k流量,註意反邊要加上k,不然無法退回
            rest -= k ; //當前節點的剩余匯入流量-k;
        }
       return flow - rest ; //遞歸完成
}

int  main()
{
    n = read() ; m = read() ; s = read() ; t = read() ;
    for( int  i = 1 ; i <= m ; ++i ){
        int  x = read() , y = read() , L = read() ;
        add( x , y , L ) ;  
    }
    int  flow = 0 ; 
    while( bfs() ){//存在增廣路
        while( flow = dinic ( s , inf ))maxflow += flow ; 
    }
    printf("%d",maxflow) ;
    return  0 ;
}

Dinic算法一定要手敲一遍板子,不然你連錯在哪都查不出來(除了機對)。


4.網絡流的應用以及ISAP算法引入

ISAP算法是EK算法的另一類優化, ISAP算法只需一次BFS即可,但代碼難度遠遠高於Dinic算法

ISAP算法復雜度在非二分圖上高於Dinic算法,在二分圖則不如Dinic算法

在此暫時不詳講,日後會更新ISAP算法詳解。

網絡流的應用

網絡流應用較廣, 但建議新手按以下順序A題,熟練算法和增強應用能力

P3376 【模板】網絡最大流

U60438 及川奈砂的蛋糕

P1129 [ZJOI2007]矩陣遊戲

P2891 [USACO07OPEN]吃飯Dining

最後是網絡流24題

網絡流深入請見這位大佬:網絡流深入

本文到此結束,若有不足,懇請大佬指出。

如果你喜歡我的文章,請大力點贊支持!感謝。

網絡流初步詳解