# Network Max Flow
## Ford-Fulkerson Algorithm
1. 使用bfs尋找最短路徑
2. 求此路徑最小能通過的水管口徑min_val
3. 把此路徑中每個節點能用的流量減掉min_val
4. 把反路徑中每個節點能用的流量加上min_val
5. 重複以上步驟直到找不到新的路徑
記錄反路徑的原因是要讓此水管的水能被重新分配,假設a b兩端,已有a流向b的水5單位,現以b當作目前所在的位置,如果想要從b流向a 2單位,那就等於在a端從原來的5單位分配2單位給當前路徑,就可以繼續流了,而b端則由(當前的水2單位)+(原有的5單位-分配出去的2單位=3單位)=5單位,還是5單位,代表此操作並不會改變之前的結果
還有一個關鍵就是找最短路徑
[模板題](https://www.luogu.com.cn/problem/P2740)
```cpp=
int rest[MAXN][MAXN],pre[MAXN],mi[MAXN];
bool bfs(int s){
queue<int> q;
memset(pre,-1,sizeof(pre)); //初始化為未走過
q.push(s);
pre[s]=s; //起點標記為走過
mi[s]=inf; //起點能用的網路流量初始為最大(為了求最小值)
while(!q.empty()){
int now=q.front();q.pop();
for(int i=1;i<=n;i++){
if(pre[i]==-1 && rest[now][i]){ //如果沒走過i且從now走到i的路還有流量
pre[i]=now; //記錄從哪裡來,以用來走inverse
mi[i]=min(mi[now],rest[now][i]); //記錄每條路徑的最小值
if(i==n) return true; //如果走到終點就直接回傳,求最短路徑
q.push(i);
}
}
}
return false; //走不到end point
}
int max_flow(){
cin >> n >> m;
for(int i=0;i<m;i++){
int u,v,w;
cin >> u >> v >> w;
rest[u][v]+=w; //流量可累計
}
int sum_flow=0;
while(bfs(1)){
int increase_flow=mi[n];
sum_flow+=increase_flow;
int now=n;
while(now!=1){
rest[pre[now]][now]-=increase_flow; //減少還能用的流量
rest[now][pre[now]]+=increase_flow; //應加inverse流量
now=pre[now]; //回朔
}
}
return sum_flow;
}
```
## 分割問題
求刪除最少數量的邊能讓1不能走到N
想法:先討論要刪除幾條邊,每次bfs找能從1走到N的一條路徑,並把它標示成走過,接著找下一條路徑直到找不到為止,這就是上面的最大流演算法,只要每一條邊視權重為1即可,這樣要刪除的邊的個數就是最大流的答案,那要刪除哪條邊呢?可視為左邊中間右邊,左半邊包含起點(1),右半邊包含終點(N),注意到中間連接的部分,每條邊一定都被標示為走過,如果沒被標示過那上面在做最大流bfs的時候就還會找到增廣路徑,所以不合法,可以把中間部分想像成腰帶,束緊了連接左右兩邊的邊,在刪除的時候要刪除這些邊即可,我們可以從1節點dfs一次可走到的節點,因為有反向流,在左半邊的任何節點都會被遍歷到,右半邊任何節點都不會被遍歷到,因為反向流堵住了中間部分,讓dfs走不過去,遍歷完後,找節點i被走過 節點j沒被走過且(i,j)之間有邊的兩個點,此邊即為答案
```cpp=
cout << sum_flow << '\n';
dfs(1);
for(int i=1;i<=n;i++){
if(vis[i]){
for(int j=1;j<=n;j++){
if(!vis[j] && has_edge[i][j]) cout << i << ' ' << j << '\n';
}
}
}
```