正文
先前已经实现了Ford-Fulkerson求最大流,但对于最大流较大的题目会超时,这时我们引入一种新的算法,Edmonds-Karp算法。该算法时Ford算法的优化,相比于Ford,可以ac洛谷模板题,且代码长度相差不大。
我们可以通过Ford-Fulkerson来学习EK算法。首先比较一下两者有什么区别。
对比项 | Ford-Fulkerson | Edmonds-Karp |
---|---|---|
增广路径选择 | 任意(DFS、随机等) | BFS(最短路径) |
时间复杂度 | O(E·f)(可能极慢) | O(VE²)(稳定) |
终止性 | 可能不终止(实数容量) | 保证终止 |
适用性 | 仅推荐整数容量 | 通用(整数/实数) |
实际效率 | 不稳定,可能快/慢 | 稳定高效 |
因此,大多数情况下在这两者间我们会选择后者(如果不会Dinic等优化方法的话)。
可见,BFS找增广路径可强制走最短路径,提高了算法的效率。
代码总体与Ford-Fulkerson求最大流相似,详细我会在代码中说明。
模板题链接
https://www.luogu.com.cn/problem/P3376
整体代码除了将DFS改为BFS以外,还要多记录pre 用来回溯路径。
我们来详细讲一下BFS为什么比DFS快。
BFS:每次选择最短的增广路径
-
Edmonds-Karp 算法(BFS 实现)每次找的是边数最少的增广路径(即最短路径)。
-
优点:
-
每次增广后,残余网络的最短路径至少增加 1 条边(数学可证明)。
-
因此,最多进行 O(VE) 次增广(V=顶点数,E=边数),总时间复杂度 O(VE²)。
-
-
避免无效搜索:不会陷入长路径的无效增广。
DFS:可能选择很长的增广路径
-
Ford-Fulkerson(DFS 实现)可能选择任意长度的路径,甚至极端情况下:
-
如果每次增广只增加 1 单位流量,且路径长度接近 V,则时间复杂度退化为 O(E·f)(f 是最大流值)。
-
若容量很大(如
f=1e18
),DFS 会极其缓慢甚至无法终止。
-
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define endl '\n'
using ll = long long;
const int N = 205;
const int INF = 1e18;
int n, m, s, t;
struct Edge {
int to, cap, rev;
};
vector<Edge> g[N];
struct Pre {
int node; // 前驱节点
int edge; // 前驱边索引
} pre[N];
bool bfs() {
memset(pre, -1, sizeof(pre));
//这样可以省去vis数组,直接判断是不是-1即可
queue<int> q;
q.push(s);
pre[s].node = -2;
//标记为-2与为访问标记-1区分
while (!q.empty()) {
int v = q.front();
q.pop();
for (int i = 0; i < g[v].size(); i++) {
Edge &e = g[v][i];
if (pre[e.to].node == -1 && e.cap > 0) {
pre[e.to] = {v, i};
if (e.to == t)
return true;
q.push(e.to);
}
}
}
return false;
}
int max_flow() {
int flow = 0;
while (bfs()) {
int minv = INF;
//回溯路径计算最小剩余容量
for (int i = t; i != s; i = pre[i].node) {
Edge &e = g[pre[i].node][pre[i].edge];
minv = min(minv, e.cap);
}
//如Ford算法中那样更新正向边和反向边
for (int i = t; i != s; i = pre[i].node) {
Edge &e = g[pre[i].node][pre[i].edge];
e.cap -= minv;
g[i][e.rev].cap += minv;
}
flow += minv;
}
return flow;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({v, w, (int)g[v].size()});
g[v].push_back({u, 0, (int)g[u].size() - 1});
}
cout << max_flow() << endl;
return 0;
}
如有疑问,欢迎评论。