ACM—網路流初步
阿新 • • 發佈:2019-01-04
本圖示最大流的一個例項。由此,可以引出最大流的一些基本的定義和概念
可以這樣看,圖就是一種管道,管道有最大通過流量的限制,圖中邊的權值就是所謂的“容量”。同時,注意有唯一的源點和匯點。
這裡需要注意容量和流量的區別。其中f(u,v)的範圍需要額外注意,是 0<= f(u,v) <= c(u,v),不會出現所謂的負流量。下圖是對可行流的圖示
有了可行流,我們還需要求最大流
那麼如何求最大流呢。可以採用著名的Ford Fulkerson演算法
所以說,演算法的關鍵在於
1)何為增廣路徑,如何找出增廣路徑。
2)如何更新流量
說的直白些,所謂增廣路徑,就是找到這樣一條路徑,其流量不滿,未達到容量上限。
所有的可能的增廣路徑在一起便構成了殘留網路。
那麼,如何增廣呢。
其實,這裡的這個描述不太準確。下面我根據我的理解再解釋一下。
第一步,計算可增加流量
設某一增廣路徑上的節點為(a1,a2,a3,a4,....,an)
如果(u,v)是正向邊,則增加流量d = min{ c(ai,aj) - f(ai,aj) | j = i +1, i =1,2,3...,n-1}
如果是逆向邊,則增加流量d = min{ f(ai, aj) | j = i +1, i =1,2,3...,n-1}
第二步,更新流量
如果(u,v)是正向邊,則 f(u,v) = f(u,v) + d
是逆向邊,則f(u,v) = f(u,v) - d
注意,如果是逆向邊,就是減法,當前管道從中減去部分流量,而且,伴隨著這部分減去的流量,必有另一部分管道的流量會增加。。而且,最後的總流量增加了d
結合上述演算法,可以詳細參閱下下列圖示
可以證明,可行流為最大流,當且僅當不存在新的增廣路徑。
總結一下最大流演算法
程式碼:
#include <bits/stdc++.h> using namespace std; const int maxn = 201; int edges[maxn][maxn]; int n,m; int ans; int father[maxn]; bool visited[maxn]; void ford_fulkerson() { ans = 0; while(1) { queue<int> Q; memset(father,-1,sizeof(father)); memset(visited,0,sizeof(visited)); visited[0] = true; Q.push(0); while( !Q.empty()) { int now = Q.front(); Q.pop(); if(now == m-1) break; for(int i = 0; i < m; i++) { if(!visited[i] && edges[now][i]) { father[i] = now; visited[i] = true; Q.push(i); } } } if( !visited[m-1] ) break; int minimal = 1000000000; for(int i = m-1; i ; i = father[i]) { if(minimal > edges[father[i]][i]) minimal = edges[father[i]][i]; } for(int i = m-1; i ; i = father[i]) { edges[father[i]][i] -= minimal; edges[i][father[i]] += minimal; } ans += minimal; } printf("%d\n",ans); } int main() { freopen("input.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { int f,t,w; memset(edges,0,sizeof(edges)); for(int i = 0; i < n; i++) { scanf("%d%d%d",&f,&t,&w); edges[f-1][t-1] += w; } ford_fulkerson(); } return 0; }