图论中一个关键的算法——最大流,很多实际问题都是这个思想,比如最大传输量、最大管道流量等等。
根据《数据结构与算法分析》和《挑战程序设计》进行综合整理。
最大流问题
设给定有向图G=(V , E),其边容量为Cv,w。这些容量可以代表通过一个管道的水的流量。图中有两个顶点:一个是s(称为发点or源点),一个是t(称为收点or汇点)。对于任意一条边e∈E,最多有“流”C(e)个单位可以通过。在不是s和t的任一顶点v,总的进入的流必须等于总的发出的流。
而最大流就是确定从s到t可以通过的最大流量。
记每条边对应的实际数据传输量为f(e)。并有0 <= f(e) <= c(e)
分析
首先考虑一个错误算法——贪心算法
- 1.找一条s到t的只经过f(e) < c(e)的边的路径,这条路径叫做增长通路
- 2.如果不存在满足条件的路径,则结束算法。否则,沿着该路径尽可能地增加c(e),返回第1步。
然而,这个算法是不确定的,因为我们是随意选择一条从s到t的路径,所以我们不能保证每次选的路径就是最好的选择,所以这个算法是欠缺的。
以此图为例:
假使源点s为顶点4,汇点t为顶点5。
1.如果选择路径4->1->3->5,该路径容许2个单位流量通过
2.然后选择路径4->0->2->5,该路径容许2个单位流量通过
3.最后选择路径4->0->3->5,该路径容许1个单位流量通过
这样,算法终止,并且结果就是最大流5。
但是如果我们刚开始直接选择路径4->0->3->5,该路径容许3个单位流量通过,但是一旦这样选择,接下来就没有从s到t的路径可以选择了,即算法终止,然后结果只是3,不是最大流,所以证实了这个算法失败了。
—————————————分割线——————————————–
为了解决这个问题,使得算法有效,我们需要让算法改变它的意向。我们通过将原先得到的流给推回去,而得到新的流。这样将之前的算法修改一下:
- 1.只利用满足f(e) < c(e)的e或者满足f(e) > 0的e对应的反向边rev(e),寻找一条s到t的路径。
- 2.如果不存在满足条件的路径,则结束。否则,沿着该路径尽可能地增加流,返回第1步。
所以在刚才的例子中,假使第一步已经选择路径4->0->3->5了,容许3个单位流量通过。
那么我们将从5开始5->3->0->4进行回推,这样在3到0之间有一条新的边(反向边)如图所示。
这样我们就又可以选择一条路径从s到t了,即4->1->3->0->2->5,并且容许2个单位的流量通过,这样我们总共就有了5个单位的流量,即最大流。
这个算法名叫Ford-Fulkerson算法。
Ford-Fulkerson算法
实现代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1000+10;
const int INF = 1000000000;
struct edge{
int to, cap, rev;
};
vector<edge> G[maxn];
bool used[maxn];
int V,E;
/*向图中增加一条从s到t为cap的边*/
void add_edge(int from, int to, int cap)
{
G[from].push_back((edge){to, cap, G[to].size()});
G[to].push_back((edge){from, 0, G[from].size() - 1});
}
/*通过DFS找增广路*/
int dfs(int v, int t, int f)
{
if(v == t) return f;
used[v] = true;
for(int i=0; i<G[v].size(); i++)
{
edge &e = G[v][i];
if(!used[e.to] && e.cap > 0)
{
int d = dfs(e.to, t, min(f, e.cap));
if(d > 0)
{
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
/*求解从s到t的最大流*/
int max_flow(int s, int t)
{
int flow = 0;
for(; ;)
{
memset(used, 0, sizeof(used));
int f = dfs(s, t, INF);
if(f == 0) return flow;
flow += f;
}
}
int main()
{
scanf("%d%d",&V,&E);
int s,t,e;
for(int i=0; i<E; i++)
{
scanf("%d%d%d",&s,&t,&e);
add_edge(s, t, e);
}
printf("%d\n", max_flow(0, V-1));
return 0;
}