网络流G=(v, E)是一个有向图,其中每条边(u, v)均有一个非负的容量值,记为c(u, v) ≧ 0。如果(u, v) ∉ E则可以规定c(u, v) = 0。网络流中有两个特殊的顶点,即源点s和汇点t。
与网络流相关的一个概念是流。设G是一个流网络,其容量为c。设s为网络的源点,t为汇点,那么G的流是一个函数f:V×V →R,满足一下性质:
容量限制:对所有顶点对u,v∈V,满足f(u, v) ≦ c(u, v);
反对称性:对所有顶点对u,v∈V,满足f(u, v) = - f(v, u);
流守恒性:对所有顶点对u∈V-{s, t},满足Σv∈Vf(u,v)=0。
本文开始讨论解决最大流问题的Ford-Fulkerson方法,该方法也称作“扩充路径方法”,该方法是大量算法的基础,有多种实现方法。
Ford-Fulkerson算法是一种迭代算法,首先对图中所有顶点对的流大小清零,此时的网络流大小也为0。在每次迭代中,通过寻找一条“增广路径”(augument path)来增加流的值。增广路径可以看作是源点s到汇点t的一条路径,并且沿着这条路径可以增加更多的流。迭代直至无法再找到增广路径位置,此时必然从源点到汇点的所有路径中都至少有一条边的满边(即边的流的大小等于边的容量大小)。
最大流最小割定理:一个网中所有流中的最大值等于所有割中的最小容量。
增广路径事实上是残留网中从源点s到汇点t的路径,可以利用图算法中的任意一种被算法来获取这条路径,例如BFS,DFS等。其中基于BFS的算法通常称为Edmonds-Karp算法,该算法是“最短”扩充路径,这里的“最短”由路径上的边的数量来度量,而不是流量或者容量。
所以BFS增广效率比DFS效率高。
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 210;
int edge[N][N], pre[N];
bool visit[N];
int m, n, si, ei, ci, result, p, i;
bool BFS(int s, int t)
{
memset(visit, false, sizeof(visit));
memset(pre, -1, sizeof(pre));
queue<int> q;
q.push(s);
visit[s] = true;
pre[s] = s;
while(!q.empty())
{
p = q.front();
q.pop();
for(i = 1; i <= m; i++)
{
if(visit[i] || edge[p][i] <= 0)
continue;
visit[i] = true;
pre[i] = p;
if(i == t)return true;
q.push(i);
}
}
return false;
}
int main()
{
while(scanf("%d %d", &n, &m) != EOF)
{
result = 0;
memset(edge, 0, sizeof(edge));
while(n--)
{
scanf("%d %d %d", &si, &ei, &ci);
edge[si][ei] += ci;//注意重边
}
while(BFS(1, m))
{
int end = m, start, minF = INT_MAX;
for(end = m; end != 1; end=start)
{
start = pre[end];
minF = min(minF, edge[start][end]);
}
for(end = m; end != 1; end = start)
{
start = pre[end];
edge[start][end] -= minF;
edge[end][start] += minF;
}
result += minF;
}
printf("%d\n", result);
}
return 0;
}