转自:https://blog.csdn.net/mystery_guest/article/details/51910913
推荐:https://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html
本图示最大流的一个实例。由此,可以引出最大流的一些基本的定义和概念
可以这样看,图就是一种管道,管道有最大通过流量的限制,图中边的权值就是所谓的“容量”。同时,注意有唯一的源点和汇点。
这里需要注意容量和流量的区别。其中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
结合上述算法,可以详细参阅下下列图示
可以证明,可行流为最大流,当且仅当不存在新的增广路径。
总结一下最大流算法
PPT链接:http://wenku.baidu.com/view/7ed3c241a8956bec0975e32b.html
代码:
#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;
}