學習筆記 - Ford-Fulkerson & EK

Ford-Fulkerson & EK - 學習筆記

以前網絡流什麼的快忘完了
老師講課的時候一臉懵逼……開始系統複習,從最大流開始
標籤:網絡流-最大流算法


『預備』

首先複習了網絡流的概念——
網絡流是一個有向圖,每一條邊有一個流量限制(也能夠叫作邊權),圖上有且僅有兩個特殊點:源點-入度爲0、匯點-出度爲0。除此以外的全部點都有出度和入度。
網絡流相似於「水流」,源點就至關於「無窮的水源」,從源點出發向其相鄰邊「流水」,邊上「水」的流量不能超過其流量限制(容量限制),且對於每一個點(除源點、匯點),流入該點的「水量」等於該點流出的「水量」(流守恆性)。
其次網絡流具備斜對稱性,即對於一條邊,若它的流量爲f,則它的反向邊流量爲 -f。數組

(下面簡寫源點爲Begin,匯點爲End,網絡的邊集爲 E、點集爲 V)
用式子的方法總結一下網絡流所具備的性質:
定義\(f(u,v)\)表示u到v的邊的流量,\(c(u,v)\)表示u到v的邊的流量限制(容量);只對相鄰的 u,v 有此定義。網絡

\[\begin{cases} f(u,v)<c(u,v)\\ f(u,v)=-f(v,u)\\ \sum_v f(u,v)=\sum_w f(w,u) \end{cases} \]

定義網絡流的 流f 爲:\(f=\sum_v f(Begin,v)=\sum_w f(w,End)\)
定義u到v的邊的殘留容量r(u,v)爲:\(r(u,v)=c(u,v)-f(u,v)\)學習

最大流即流網絡中的最大流值。
EK算法是對Ford-Fulkerson方法的實現。ui


『Ford-Fulkerson方法』

「殘留網絡&增廣路」

若是 \(r(u,v)>0\),則邊 (u,v) 在殘留網絡中。
增廣路是殘留網絡中從 Begin 到 End 的一條路徑 P,\(\delta(P)=\min\{r(u,v)\},(u,v)\in P\) 表示增廣路P的殘留容量。spa

「方法」

Ford-Fulkerson方法的主要思想是先構造殘餘, DFS 找到一條增廣路,而後找到增廣路上的流量 \(\delta\),將增廣路上的每一條邊的流量限制都減去 \(\delta\)、每一條邊都反向邊都流量限制都加上 \(\delta\)
爲何要把反向邊的流量限制加上 \(\delta\) 呢?咱們來看一個簡單的例子:
FlowGraph
若是咱們一開始選擇流「1-2-3-4」,那麼咱們獲得流的大小爲1,可是顯然咱們能夠選擇流「1-2-4 , 1-3-4」,流的大小爲2。
若是先流「1-2-3-4」,那麼咱們就至關於肯定了邊 (2,3) 必須流,可是這是不必定的。咱們給它的反向邊 (3,2) 加上 \(\delta\)(最初全部邊的反向邊的流量限制都爲0),那麼下一次咱們流過它的反向邊 (3,2) 時,就至關於「撤銷」了流過 (2,3)。而後兩次的流量之和就是最大流。
爲何這樣是正確的?
讓咱們手推一下這張圖的最大流過程:先找到了「1-2-3-4」,而後 (1,2)(2,3)(3,4) 的流量限制減去10,(2,1)(3,2)(4,3) 加上10;再流過 「1-3-2-4」。就至關於把原來「1-2-3-4」的(2,3)斷開,接上(2,4)造成「1-2-4」;而斷開事後的「2-3-4」中,流過反向邊(3,2)使得(2,3)的流被撤銷,再接上(1,3)就造成「1-3-4」。code


『EK算法』

一種比較基礎的對Ford-Fulkerson方法的實現,基於BFS。
首先定義一些數組,方便後面闡述:blog

(1) vis[u] 表示點 u 在該次BFS中是否被訪問過
(2) flw[u] 表示該次BFS中從源點開始流到點 u 的流最大的值(也就是源點BFS到u的路徑上的邊的最小的流量限制)
(3) preedg[u] 表示該次BFS中是從哪一條邊流到u的隊列

「BFS部分」

從源點開始,BFS遍歷整個流網絡,要求通過的有向邊的流量限制嚴格大於0,而且不重複通過同一個點。當遍歷到匯點時,返回當前流值。
假設如今要從u流到v,先要保證 vis[v] == 0 而且 (u,v) 的流量限制大於0。而後標記 vis[v] ,再記錄 preedg[v] 爲當前邊的編號;flw[v] 的計算相似於 dp,由於流值最大不超過 (u,v) 的限制,那麼遞推式也很是顯然 \(flw[v]=\min\{flw[u],c(u,v)\}\)
若是v就是匯點,則返回 flw[v],不然將v壓入隊列。
若是最後沒法到達匯點,則返回0,表示無增廣路。get

「EK算法主部分」

先BFS判斷當前是否有增廣路,若是有,則BFS返回值則爲增廣路的流量 \(\delta\),則從v沿着增廣路倒過來回到源點,並將增廣路上的邊的流量限制減去 \(\delta\),增廣路上的邊的反向邊的流量限制加上 \(\delta\) 。直到沒有增廣路(BFS返回值爲0),退出循環。
其實就是把 Ford-Fulkerson 方法模擬了一遍。
所以EK算法的時間複雜度並不理想,但畢竟它是(彷佛是)最大流算法中最爲穩定的算法,有其存在的價值。

「模板代碼」

〔洛谷 P2740〕爲原題的代碼~

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=200;
struct FLOWGRAPH{
    struct NODE{
        int to,nxt,lim,rev;
        NODE(){}
        NODE(int _to,int _nxt,int _lim,int _rev):
            to(_to),nxt(_nxt),lim(_lim),rev(_rev){}
    }nod[N*2+7];
    int cnt,adj[N+7];
    void Rebuild(){
        memset(adj,-1,sizeof adj);
        cnt=0;
    }
    void AddEdge(int u,int v,int lim){
        int A=++cnt,B=++cnt;
        nod[A]=NODE(v,adj[u],lim,B),adj[u]=A;
        nod[B]=NODE(u,adj[v],0,A),adj[v]=B;
    }
}grp;
int m,n;
int preedg[N+7],flw[N+7],vis[N+7];
int BFS(int cas){
    queue<int> que;
    que.push(1);
    flw[1]=(1<<30);
    while(!que.empty()){
        int u=que.front();que.pop();
        for(int i=grp.adj[u];i!=-1;i=grp.nod[i].nxt){
            int v=grp.nod[i].to;
            if(!grp.nod[i].lim || vis[v]==cas) continue;
            vis[v]=cas;
            preedg[v]=i;flw[v]=min(grp.nod[i].lim,flw[u]);
            if(v==n) return flw[v];
            que.push(v);
        }
    }
    return 0;
}
int EK(){
    int del,res=0,cas=0;
    while(del=BFS(++cas)){
        int pnt=n;
        while(pnt!=1){
            grp.nod[preedg[pnt]].lim-=del;
            grp.nod[grp.nod[preedg[pnt]].rev].lim+=del;
            pnt=grp.nod[grp.nod[preedg[pnt]].rev].to;
        }
        res+=del;
    }
    return res;
}
int main(){
    grp.Rebuild();
    scanf("%d%d",&m,&n);
    for(int i=0;i<m;i++){
        int u,v,lim;
        scanf("%d%d%d",&u,&v,&lim);
        grp.AddEdge(u,v,lim);
    }
    int res=EK();
    printf("%d\n",res);
    return 0;
}

\(\mathcal{The\ End}\)

\(\mathcal{Thanks\ For\ Reading!}\)

若是有沒看懂或者有問題的,請諮詢做者郵箱\(lucky\_glass@foxmail.com\)~

相關文章
相關標籤/搜索