最大流_FF思想_EK算法
Posted 一只特立独行的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大流_FF思想_EK算法相关的知识,希望对你有一定的参考价值。
原题链接
问题
图论中有一个重要问题,假设有一个水管网图,每根水管都规定了一个可以流的方向,从v号结点到u号结点最大允许通过的水量为c(v,u),有一个源点s存储了无限多的水,有一个汇点可以存储无限多的水。问:从源点流向汇点的水流的最大速率是多少?
思路
采用经典的EK算法,时间复杂度为O(n*m^2)n为结点数,m为边数,虽然上界很高,但是一般情况很能到上界,一般的情况要好的多。
四个概念,一个定理:
网络流的概念和定理非常多,为了不混淆,先只讲两个会用到的定理。
1.反向边:
这个概念很好理解,对于图中的每一条有向边,如果存在一条方向相反的边,这条边就是反向边。
2.可行流:
从s流向t的任意一个满足条件的流(不一定是最大流)。
3.残留网络:
在图中找到任意一条可行流,并将该可行流的路径上的每一条边都建立一个反向边。 反向边的权值是可行流的权值,然后将正向边的权值更新为原来的权值-可行流的权值。
4.增广路径:
在当前的图中,存在一条从s到t的可行流路径。
定理1:
如果f是最大流,
G
f
G_f
Gf中一定不存在增广路径。
证明:
因为
f
可
行
流
+
f
增
广
路
径
=
f
可
行
流
f_{可行流}+f_{增广路径}=f_{可行流}
f可行流+f增广路径=f可行流
如果f是最大流,要满足上式,
f
增
广
路
径
=
0
f_{增广路径}=0
f增广路径=0,所以不存在增广路径。
FF思想
这是一种找最大流的思想,但是他有很多中算法实现。
1.找到一条从s到t的可行流路径。
2.记录可行流的大小。
2.根据可行流路径建立增广网络。
3.重复上述1到3,直到没有增广路径。
FF思想有很多严格的证明方法,我这里采用最直观但是相对没那么严谨的方法。
假设s到t有两条最大流,分别如下。
但是在寻找最大流时,我们的运气不好,本来应该经过A点到达t点,但是却在A点进行了拐弯,从下面到达了t点。按理来说这样的话,下面的最大流我们永远也无法找到了。但是,我们有在找到一条可行流时,会建立反向边,所以,这时候s到B时,还可以往上走,抵消掉了A->B的水流,从而到达了t点。所以,这个算法可以找到最大流。
EK算法
EK算法,包括dinic算法都是基于FF思想的一种算法。是FF思想的具体实现,但是,EK算法是最简答的一种算法。
1.bfs遍历图,找到一条可行流,累加可行流。
2.更新残余网络。
3.判断是否存在增广路,如果存在,重复1,2。
4.输出累加的可行流,即最大流。原理参考
f
可
行
流
+
f
增
广
路
径
=
f
可
行
流
f_{可行流}+f_{增广路径}=f_{可行流}
f可行流+f增广路径=f可行流
代码:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#define debug(x) cout<<x<<endl;
using namespace std;
typedef long long LL;
const int N = 205, M = 205, INF = 0x3f3f3f3f;
LL G[N][M];//图
LL flow[N], pre[N];//结点的流,前驱
int n, m;//结点数,边数
int bfs(int s,int t) {
memset(pre, -1, sizeof pre);
memset(flow, 0, sizeof flow);
queue<int > q;
flow[1] = INF;//代表最多可以流出的水量
q.push(s);
while (q.size()) {
int v = q.front(); q.pop();
for (int i = 1; i <= n; i++) {
if (G[v][i] > 0&&pre[i]==-1) {
q.push(i);
pre[i] = v;
flow[i] = min(flow[v],G[v][i]);
}
}
if (v == t) break;
}
return flow[t];
}
LL EK(int s, int t) {
LL tol = 0;
LL res;//存放流入汇点的数值
while (1){
//存在增广路
res = bfs(s, t);
if (res == 0)break;
int p = t;
while (p != s) {
//更改残留图
G[pre[p]][p] -= res;
G[p][pre[p]] = res;//回流
p = pre[p];
}
tol += flow[t];
}
return tol;
}
int main() {
while (~scanf("%d%d", &m, &n)) {
memset(G, 0, sizeof G);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
G[a][b] += c;
}
printf("%lld\\n", EK(1, n));
}
return 0;
}
/*
test1:
7 6
1 2 40
1 4 30
2 3 30
2 5 30
4 5 40
3 6 40
5 6 50
70
test2:
7 6
1 2 5
1 4 6
2 3 4
4 5 4
2 5 2
3 6 3
5 6 7
9
test 3
1 2
1 2 10
*/
以上是关于最大流_FF思想_EK算法的主要内容,如果未能解决你的问题,请参考以下文章