网络流之最大流(Edmonds-Karp)

正文

先前已经实现了Ford-Fulkerson求最大流,但对于最大流较大的题目会超时,这时我们引入一种新的算法,Edmonds-Karp算法。该算法时Ford算法的优化,相比于Ford,可以ac洛谷模板题,且代码长度相差不大。

我们可以通过Ford-Fulkerson来学习EK算法。首先比较一下两者有什么区别。
对比项Ford-FulkersonEdmonds-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;
}

如有疑问,欢迎评论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值