---
# System prepended metadata

title: '***Edmonds-Karp 演算法***'

---

# ***Edmonds-Karp 演算法***

在閱讀本篇前，讀者需要先大致知道 *get maximun flow* , *residual network* 及 *flow network* 的內容
本篇將介紹 *Edmonds-Karp* 如何運作 , 並證明他的 *time complexity* , 最後附上c++ code

## ***augmenting path***
s 為 source , t 為 tink

f : *flow network* 中的 flow
G~f~ : *redidual network*
c~f~ : *residual network* 中的 capacity
E~f~ : *residual network* 中存在的 edge，若 $c_f(u,v) > 0$ 則 $(u,v) \in E_f$
*augmention* : 要增加多少 flow from $s$ to $t$

在 *residual network* 中，若可以找到一條 $s$ ~ $t$ 的路徑 $p$
則稱此 $p$ 為一條 *augmenting path*
並且，*residual network* 可能同時擁有1條以上的 *augmenting path*

若 $p$ 存在，代表還有多的 flow 可以從 $s$ 流向 $t$
定義 $c_f(p) = min\  \{ c_f(u,v) : (u,v)\ is \ on \ p \}$
表 $p$ 最多能有 $c_f(p)$ 從 $s$ 流向 $t$
也就是 *augmention* 的值

$p_1$ $(s-2-1-t)$ 為一條 *augmenting path*
$c_f(p_1) = 4$
![](https://i.imgur.com/19aSeyL.png =50%x)



###### 上圖為初始的 flow network 數值為 flow/capacity

## ***Ford-Fulkerson method***
也有人會稱作 *Ford-Fulkerson* *algorithm*
算是 *Edmonds-Karp* 的前身
流程與 *Edmonds-Karp* 幾乎相同，只有選擇 *augmenting path* 上有差
*Ford-Fulkerson* 無限制如何選擇 *augmenting path*

以下以一個 *flow network* 作範例
用 *Ford-Fulkerson* 並求出他的 *maximun flow*
(此圖參考CLRS)

![](https://i.imgur.com/uYtJgc8.png =50%x) 原始 *flow network*

左為 *capacity network* / 右為 *flow network*
紅線為選擇的 *augmenting path*

#

![](https://i.imgur.com/O4361Ws.png =48%x) ![](https://i.imgur.com/z8cBqR5.png =50%x)

以 $c_f(p)$ 為增加 flow 做 *augmention*，則 $(s,1)$ $(1,3)$ $(3,2)$ $(2,4)$ $(4,t)$ 之 flow 皆+4
右圖 augmention 後的 *flow network*

#

![](https://i.imgur.com/qxkksyU.png =48%x) ![](https://i.imgur.com/ry2GgSO.png =48%x)

因上個 *augmention* 中 flow 增加了4，因此 $c_f(s,1)\  c_f(1,3) \  c_f(3,2) \  c_f(2,4) \  c_f(4,t)$ 皆-4

而 $c_f(4,t) = 0$ 故 $(4,t)$ 消失於 $E_f$ 中
反之，$c_f(1,s)\  c_f(3,1) \  c_f(2,3) \  c_f(4,2) \  c_f(t,4)$ 皆+4，故新增於 $E_f$ 中

#

![](https://i.imgur.com/jGOdyi6.png =48%x) ![](https://i.imgur.com/jBRwDnL.png =48%x)

此 *augmention*，1往2的flow增加了4，故在右圖中，2往1的flow由4歸零

#


![](https://i.imgur.com/kEX6jPs.png =48%x) ![](https://i.imgur.com/Tgb4Jah.png =48%x)

#
![](https://i.imgur.com/gH0lNoo.png =48%x) ![](https://i.imgur.com/qXuLP9U.png =48%x)
#

![](https://i.imgur.com/QuR2KtT.png =50%x)  無 *augmenting path* 存在
#

由最後一張右圖可知 *maximun flow* = 23



## **不佳的 *aumenting path***

一個 *residual network* 可能存在一個以上的 *aumenting path*
如何選擇 *augmenting path* 會影響到演算法的執行效率
以下示範不好的 *augmenting path* : 
###### 下圖皆為 residual network
##



![](https://i.imgur.com/QozHBM0.png =50%x)
選擇 $s-2-1-t$ 為 path，並增加 1 flow from s to t

##

![](https://i.imgur.com/IFt8P89.png =50%x)
選擇 $s-1-2-t$ 為 path，並增加 1 flow from s to t

##

![](https://i.imgur.com/mPNyy1A.png =50%x)
選擇 $s-2-1-t$ 為 path，並增加 1 flow from s to t

##

如此重複做下去，需要2000次才能結束 (得到 *maximun flow* )
因此，*Ford-Fulkerson* 的時間複雜度與邊的 capacity 有關
也就是可能選到沒效率的 *augmenting path*

*Edmonds-Karp* 利用 *bfs* 解決了這個問題，並把時間複雜度限制在 $O(VE^2)$

## **BFS**
在 *Edmonds-Karp* 中，
利用 *bfs* 尋找 *residual network* 中 s to t 的 *shortest path* 
(此路徑長度取決於經過多少個邊)
因為將此 *shortest path* 作為 *augmenting path*，較有效率

為了證明這樣能將時間複雜度限制在 $O(VE^2)$，需先證明一個定理 : 

#### **Lemma1** :
在 *residual network* 中，對於所有 $v \in V - \{s,t\}$
$s$ 到 $v$ 的最短距離 $\delta_f(s,v)$ 會隨著 *augmention* 單調遞增

##

#### *proof* :
現在有一個 *augmention* : AG 進行
$f$ 為在 AG 之前的 flow
$f'$為在 AG 之後的 flow
所要證明為 $\delta_{f'}(s,v) \ge \delta_f(s,v)$

這裡將使用反證法
假設 $v$ 為其中一個在 AG 後 $\delta(s,v)$ 減少的點
$\delta_{f'}(s,v) < \delta_f(s,v)$

令 $p=s \rightarrow ... \rightarrow u \rightarrow v$ 為 $v$ 跟 $u$ 之 *shortest path* in $G_{f'}$ , 可得到 : 

$(u,v) \in E_{f'}$
$\delta_{f'}(s,u) = \delta_{f'}(s,v) - 1$
不失一般性 : $\delta_{f'}(s,u) \ge \delta_f(s,u)$

接下來，分別討論兩種狀況，分別是 $(u,v) \in E_f\ 和\ (u,v) \not\in E_f$

##

$(u,v) \in E_f \ :$


$\delta_f(s,v) \le \delta_f(s,u)+1$
$\ \ \ \ \ \ \ \ \ \ \ \  \le \delta_{f'}(s,u)+1$
$\ \ \ \ \ \ \ \ \ \ \ \  = \delta_{f'}(s,v)$

$\Rightarrow\delta_f(s,v) \le \delta_{f'}(s,v)$

與所假設矛盾

##

$(u,v) \not\in E_f \ :$

若 $(u,v) \not\in E_f \ 但\ (u,v) \in E_{f'}$
則 $G_{f}$ 中的 *augmenting path* 必定包括 $(v,u)$
這樣 $c_{f'}(u,v)$ 才會 > 0

$\therefore(v,u) \in E_f$ 且 $\delta_{f}(s,v) = \delta_{f}(s,u) - 1$  (因為*augmenting path* 為 *shortest path)*

$\delta_{f}(s,v) = \delta_{f}(s,u) - 1$
$\ \ \ \ \ \ \ \ \ \ \ \  \le \delta_{f'}(s,u)-1$
$\ \ \ \ \ \ \ \ \ \ \ \  = \delta_{f'}(s,v)-2$

$\Rightarrow\delta_f(s,v) \le \delta_{f'}(s,v)-2$

與所假設矛盾

## 

根據以上兩種種況皆矛盾，可知道此種 $v$ 點不存在

而之所以能有這個性質是因為所選 augmenting path 為 shortest path

有了此 lemma 就能夠證明 *Edmonds-Karp* 的時間複雜度

## **時間複雜度證明**

#### **Theorem1** :
在 *Edmonds-Karp* 中，augmentions 的數量為 $O(VE)$
也就是找了幾次 *augmenting path*

##

#### *proof* :

$(u,v) \in E_f$
我們稱 $(u,v)$ is critical 若 $c_p(u,v)=c_p(p)$ , $p$ 為 *augmenting path*
換句話說，就是 $(u,v)$ 在 $p$ 擁有最小的 capacity

令 $(u,v)$ is critical
$\therefore\delta_{f}(s,u) = \delta_{f}(s,v) - 1$
因為 $(u,v)$  critical , 在 *augmention* 後 , $c_f(u,v)=0$, $(u,v)$會消失在 *residual network*

若 $(u,v)$ 要再次出現在 $G_f$ 中，需要有一次的 *augmention* 且 $(v,u)$ 在 *augmention path* 上


假設這時的 flow 為 $f'$
$\therefore\delta_{f'}(s,v) = \delta_{f'}(s,u) - 1$

$\delta_{f'}(s,u) = \delta_{f'}(s,v) + 1$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ge \delta_f(s,v)+1 \ \ :\ \  lemma1$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ = \delta_{f}(s,u)+2$

$\Rightarrow\delta_{f'}(s,v) \ge \delta_{f}(s,v)+2$


- $v \in V-\{s,t\}$ 的 $\delta_f(u,v)$ 最大值為  $|V|-2$
- 間隔兩次 critical edge，$\delta_f(u,v)$ 至少增加 2

$\Rightarrow$ 一條 $(u,v)$ 成為 *critical edge* 的次數最多為 $(|V|-2)/2$ , 即$O(V)$

所以在一次 *Edmonds-Karp* 中共有 $O(VE)$ 條 *critical edge*
又每次 *augmention* 中，為 *critical edge* 的數量為常數
因此 *augmention* 的次數為 $O(VE)$

#

由以上 Thereom，*augmention* 的數量為 $O(VE)$
又每次 *augmention* 時的 bfs 時間複雜度為 $O(V)$
故 *Edmonds-Karp* 的時間複雜度為 $O(VE)*O(E)=O(VE^2)$


## **code**
```CPP=
#include <iostream>
#include <vector>
#include <queue>
#include <time.h>
using namespace std;

//this time complexity is O(VE^2)

struct node {
    int val;
    node *next;
    node (int x, node *t) {
        val = x;
        next = t;
    }
};

class Solution {
public:
    vector<vector<int>> cf;  //點到點之間能走的capcity
    vector<node*> adj_list;
    vector<int> preV;  //augment path時的路徑的點的前一個點

    vector<vector<int>> *edges;
    int n;
    
    Solution(int nt, vector<vector<int>> *edgs) {
        n = nt;
        edges = edgs;
    }

    void initial(int s, int t) {
        cf = vector<vector<int>>(n, vector<int>(n,0));
        adj_list = vector<node*>(n, nullptr);
        preV = vector<int>(n, -1);

        for(vector<int> &edge : (*edges)) {
            cf[edge[0]][edge[1]] = edge[2];
            add_in_adj(edge[0], edge[1]);
        }
    }

    int get_max_flow(int s, int t) {
        initial(s,t);
        int new_flow, max_flow = 0;

        while((new_flow = bfs(n,s,t)) != -1) {  //s~t 還存在 path
            augmention(new_flow, s, t);        
            max_flow += new_flow;
        }

        return max_flow;
    }

    int bfs(int n, int s, int t) {  //回傳 cf(p) , p 為 augmenting path
        queue<int> que;
        que.emplace(s);
        vector<bool> visit(n, false);
        visit[s] = true;

        while(!que.empty()) {
            int u = que.front();
            que.pop();
            
            for(node *cur = adj_list[u]; cur; cur = cur->next) {
                int v = cur->val;
                if(!visit[v])
                {
                    visit[v] = true;
                    preV[v] = u;      //在 augemnting path裡，v的前一個點是u
                    que.emplace(v);

                    if(v == t)  //已經找到 augment path
                        return find_min_in_augpath(s,t);
                }
            }
        }
        
        return -1;
    }

    int find_min_in_augpath(int s, int t) {
        int cur = t, ret = INT32_MAX;
        while(cur != s) {
            ret = min(ret, cf[preV[cur]][cur]);
            cur = preV[cur];
        }
        return ret;
    }

    void augmention(int val, int s, int t) {  // val is the minimun value in augment path
        int cur = t;

        while(cur != s)
        {
            cf[preV[cur]][cur] -= val;
            cf[cur][preV[cur]] += val;

            if(cf[preV[cur]][cur] == 0)
                del_in_adj(preV[cur], cur);
            if(cf[cur][preV[cur]] == val)
                add_in_adj(cur, preV[cur]);

            cur = preV[cur];
        }
    }

    void del_in_adj(int u, int v) {
        node *cur = adj_list[u];

        if(cur->val == v)
            adj_list[u] = cur->next;
        else {
            while(cur->next->val != v)
                cur = cur->next;
            cur->next = cur->next->next;
        }
    }

    void add_in_adj(int u, int v) {
        node *tmp = new node(v, adj_list[u]);
        adj_list[u] = tmp;
    }
};


int main(void) {
    int n, m;
    cin >> n >> m;  //vertexs and edges num
    int u,v,cap,s,t;
    vector<vector<int>> edgs(m);
    
    for(int i=0; i<m; i++) {
        cin >> u >> v >> cap;
        edgs[i] = vector<int>{u,v,cap};
    }

    s=0, t=n-1;

    double start = clock();
    Solution S = Solution(n,&edgs);
    cout << "the maximun flow is : " << S.get_max_flow(s,t) << '\n';
    double end = clock();
    cout << "花費時間(秒) : " << (end - start)  / CLOCKS_PER_SEC ;

    return 0;
}
```

這是本人第一次寫hackmd，也是第一次在網路上發表自己的code
若有任何想法問題或需要改進的地方可以告訴我