彻底讲透-最大流量

最大流量(Maximum Flow)可以一句话理解为:

“把水管网络看成图,求从进水口到出水口最多能同时流过多少水,且每根水管都有流量上限。”

下面用 生活故事 + 手算动画 + 相关算法 + 代码 给你彻底讲透。


🚰 生活故事:自来水厂到小区

  • 源点 s:自来水厂

  • 汇点 t:你家小区

  • 中间管道:每根水管有 最大容量

  • 目标:在不爆管的前提下,最多能送多少水

🖐️ 手算动画

  • 🧩 手算小图

边 & 容量(吨/小时)
S→A  10
S→B   8
S→C  12     
A→T  15
B→T  10
C→D   6    
D→T   9    

问题:从 S(自来水厂)到 T(你家)最多能同时送多少吨?

  • 🖐️ 手算步骤(像给水管贴标签)

① 先画空图,所有边标 剩余容量
S→A  10/10
S→B  8/8
S→C  12/12
A→T  15/15
B→T  10/10
C→D  6/6
D→T  9/9

(斜杠左边是 已用流量,右边是 剩余容量


② 找第一条增广路径(随便挑)

路: S → A → T
瓶颈 = min(10, 15) = 10

把整条路径 减 10

S→A  10/0   (已用 10,剩余 0)
A→T  10/5   (已用 10,剩余 5)

总流量 = 10


③ 再找第二条增广路径

路: S → B → T
瓶颈 = min(8, 10) = 8

更新:

S→B  8/0
B→T  8/2

总流量 = 10 + 8 = 18


④ 再找第3条增广路径

  • 路: S → C → D → T
    瓶颈 = min(12,6,9) = 6

    更新:

    S→C  6/6
    C→D  6/0
    D→T  6/3

    总流量 = 18 + 6 = 24


⑤ 检查还有路可走吗?
  • S→A 已用完

  • S→B 已用完

  • S→C 还剩 6,但 C→D 已用完,无法到达 T

再也找不到新路径 → 结束!


  • ✅ 结论

增广路径流量
S→A→T10
S→B→T8
S→C→D→T6
最大流量24

  • 🧠 记忆口诀

“一条路一条路地找,每次只塞 最细水管 的容量,直到再也塞不进为止。”

🧮 官方算法

算法思路复杂度一句话
Edmonds-Karp (EK)BFS 找增广路径O(V·E²)每次找最短增广路
Dinic分层图 + 多路增广O(V²·E)现代竞赛/工程首选
ISAPDinic 的常数优化≈ Dinic竞赛模板

📐 分层(BFS 建层)

生活动作
把每个路口按“离水厂多少站”贴楼层号:

S(0) → A(1) → T(2)
  ↘  B(1) → T(2)
  • 只能从低层到高层走,防止 兜圈子(成环)。

  • 分层一次 O(V+E)。


🔍 多路增广(DFS 塞水)

生活动作
从水厂出发,沿着“下一层”的路口,能塞多少塞多少,直到塞不动为止。

实时操作

  1. 找出 瓶颈容量(路径上最细的水管)。

  2. 把整条路径的流量 减掉瓶颈,同时在 反向边加上瓶颈(方便反悔)。

  3. 累加总流量。

下面给出 Dinic 模板,因为它 好写 + 快 + 通用


💻 Dinic 算法详解

阶段动作路径瓶颈累计流量剩余图简述
0初始0所有边满容量
1分层+BFSS(0)→A(1)→T(2)1010S→A 0/10;A→T 5/15
2分层+BFSS(0)→B(1)→T(2)818S→B 0/8;B→T 2/10
3分层+BFSS(0)→C(1)→D(2)→T(3)624S→C 6/12;C→D 0/6;D→T 3/9
4无法再分层24结束
import java.util.*;

public class MaxFlowDemo {
    /* ====== Edge ====== */
    static class Edge {
        int to, rev, cap;
        Edge(int t, int r, int c) { to = t; rev = r; cap = c; }
    }

    /* ====== Dinic ====== */
    static class Dinic {
        List<Edge>[] g;
        int[] level, ptr;
        Dinic(int n) {
            g = new ArrayList[n];
            for (int i = 0; i < n; i++) g[i] = new ArrayList<>();
            level = new int[n];
            ptr = new int[n];
        }

        void addEdge(int u, int v, int cap) {
            g[u].add(new Edge(v, g[v].size(), cap));
            g[v].add(new Edge(u, g[u].size() - 1, 0));
        }

        boolean bfs(int s, int t) {
            Arrays.fill(level, -1);
            Queue<Integer> q = new LinkedList<>();
            level[s] = 0; q.add(s);
            while (!q.isEmpty()) {
                int u = q.poll();
                for (Edge e : g[u])
                    if (e.cap > 0 && level[e.to] == -1) {
                        level[e.to] = level[u] + 1;
                        q.add(e.to);
                    }
            }
            return level[t] >= 0;
        }

        int dfs(int u, int t, int flow) {
            if (u == t) return flow;
            for (; ptr[u] < g[u].size(); ptr[u]++) {
                Edge e = g[u].get(ptr[u]);
                if (e.cap > 0 && level[e.to] == level[u] + 1) {
                    int f = dfs(e.to, t, Math.min(flow, e.cap));
                    if (f > 0) {
                        e.cap -= f;
                        g[e.to].get(e.rev).cap += f;
                        return f;
                    }
                }
            }
            return 0;
        }

        int maxFlow(int s, int t) {
            int flow = 0;
            while (bfs(s, t)) {
                Arrays.fill(ptr, 0);
                int f;
                while ((f = dfs(s, t, Integer.MAX_VALUE)) > 0) flow += f;
            }
            return flow;
        }
    }

    /* ====== 测试 ====== */
    public static void main(String[] args) {
        int n = 6; // 0:S, 1:A, 2:B, 3:C, 4:D, 5:T
        Dinic mf = new Dinic(n);
        /* 按题目输入建图 */
        mf.addEdge(0, 1, 10);  // S→A
        mf.addEdge(0, 2, 8);   // S→B
        mf.addEdge(0, 3, 12);  // S→C
        mf.addEdge(1, 5, 15);  // A→T
        mf.addEdge(2, 5, 10);  // B→T
        mf.addEdge(3, 4, 6);   // C→D
        mf.addEdge(4, 5, 9);   // D→T

        System.out.println("最大流量 = " + mf.maxFlow(0, 5)); // 输出 24
    }
}

✅ 记忆口诀

最大流 = 反复找还能塞水的路,直到塞满;Dinic 用“分层+多路 DFS”一次塞饱。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sevenlumos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值