本站内容由 AI 生成,可能存在错误。如发现问题,欢迎到 GitHub Issues 反馈。

网络流:管道能通多少?

网络流:管道能通多少?

更新于 2026-04-24

Part 2”量”建立了度量工具:最短路径衡量两点之间有多远,中心性判断谁最重要,社区发现找出谁和谁抱团。但”量”回答的是图是什么样的——描述性的。现在我们进入 Part 3”优”:图上该怎么做——决策性的。

上一篇 Art. 11: 最小生成树用贪心策略以最小代价连通所有节点,是 Part 3 的第一站。本文是第二站——给定一个有容量限制的网络,从源头到目的地,最多能输送多少?瓶颈在哪? 这就是网络流问题。MST 关注”最便宜的连通”,本文关注”最大的流量”。而且我们将看到,最大流和最小割之间有一个极其优美的对偶关系——最大流量恰好等于最窄瓶颈截面的总容量。

算法范式:增广路(Ford-Fulkerson, Edmonds-Karp, Dinic)、对偶性(Max-flow = Min-cut)

核心问题:管道能通多少?

想象一个管道网络:水从水源 ss(source,源)出发,流经若干中转站,最终汇入水库 tt(sink,汇)。每段管道有一个容量上限——它每秒最多能通过多少水。问题是:sstt,整个网络每秒最多能输送多少水?

这个问题远不止供水网络。把”水”换成数据包,就是通信网络的带宽分析;换成货物,就是供应链的吞吐量优化;换成电流,就是电路的负载分析。网络流是图上组合优化的核心工具,后续的匹配图划分都是它的特例或推广。

流网络的定义

形式化地,一个流网络(flow network)是一个有向图 G=(V,E)G = (V, E),附带:

  • 容量函数 c:ER+c : E \to \mathbb{R}^+,每条边 ee 的容量 c(e)0c(e) \geq 0
  • 一个源点 sVs \in V(只出不进)和一个汇点 tVt \in V(只进不出)

一个可行流(feasible flow)是一个函数 f:ER+f : E \to \mathbb{R}^+,满足两个约束:

  1. 容量约束:对每条边 ee0f(e)c(e)0 \leq f(e) \leq c(e)(流量不能超过容量)
  2. 流量守恒:对每个非源非汇节点 vv,流入 vv 的总流量 = 流出 vv 的总流量

(u,v)Ef(u,v)=(v,w)Ef(v,w),vV{s,t}\sum_{(u,v) \in E} f(u,v) = \sum_{(v,w) \in E} f(v,w), \quad \forall v \in V \setminus \{s, t\}

流的(value)f|f| 定义为从源点 ss 流出的净流量:

f=(s,v)Ef(s,v)(v,s)Ef(v,s)|f| = \sum_{(s,v) \in E} f(s,v) - \sum_{(v,s) \in E} f(v,s)

最大流问题就是找一个可行流 ff,使得 f|f| 最大。

流网络:容量与流量流网络 — 容量/流量/守恒8/106/85/73/49/105/90/39/12sABCDtflow/capacity (流量/容量)已饱和 (flow = capacity)源(Source)汇(Sink)

上图展示了一个流网络的例子。每条边上标注的 flow/capacity 表示当前流量和容量上限。红色边已经饱和(流量 = 容量),灰色边没有流量。源点 ss(绿色)只有出边,汇点 tt(红色)只有入边。

最大流最小割定理

在讲算法之前,先理解一个贯穿全文的核心定理——它不仅指导算法设计,还揭示了最大流和网络瓶颈之间的深刻联系。

什么是割?

ss-tt(cut)是把节点集 VV 分成两个不相交的子集 SST=VST = V \setminus S,使得 sSs \in StTt \in T。割的容量是所有从 SSTT 的边的容量之和:

c(S,T)=uS,vT,(u,v)Ec(u,v)c(S, T) = \sum_{u \in S, v \in T, (u,v) \in E} c(u, v)

注意:只计算从 SSTT 方向的边,不包括从 TTSS 的边。

直觉上,割就是一刀切断网络,把源和汇分开。割的容量就是这一刀切断的所有管道的总容量——它是流量的一个上界,因为所有从 sstt 的流都必须通过这些被切断的管道。

定理:最大流 = 最小割

最大流最小割定理(Max-flow Min-cut Theorem, Ford & Fulkerson, 1956):

maxff=minS,Tc(S,T)\max_f |f| = \min_{S,T} c(S,T)

即:最大流的值恰好等于最小割的容量。

最大流 = 最小割最大流 = 最小割 (Max-flow Min-cut)最小割ST10874109312sABCDt最大流 = 10 + 8 = 18 = 最小割容量割 S = {s}, T = {A,B,C,D,t},切断边 c(s,A)+c(s,B) = 18

为什么成立? 两个方向:

  1. maxfminc(S,T)\max |f| \leq \min c(S,T)(弱对偶):对任意割 (S,T)(S, T) 和任意流 ff,所有流都要从 SS 流到 TT,受限于割的容量。因此任意流的值不超过任意割的容量。
  2. maxfminc(S,T)\max |f| \geq \min c(S,T)(强对偶):当不存在增广路时(稍后定义),可以构造一个割,其容量等于当前流的值。这就证明了等式成立。

具体地,当 Edmonds-Karp 或 Dinic 算法终止时,从 ss 出发在残余图中能到达的节点集合就是 SS,其余节点就是 TT——这个割的容量恰好等于算法找到的最大流值。

这个定理的威力在于对偶性:求最大流(一个优化问题)等价于求最小割(看似完全不同的一个组合问题)。这种”一个问题的最优解 = 另一个互补问题的最优解”的模式在组合优化中反复出现。

Ford-Fulkerson 方法

残余图(Residual Graph)

要设计算法,我们需要一个关键工具——残余图(residual graph)GfG_f。给定当前流 ff,残余图描述了”每条边还能再推多少流”。

对于原图中每条边 (u,v)(u, v)(容量 c(u,v)c(u,v),当前流 f(u,v)f(u,v)),残余图 GfG_f 包含:

  • 正向残余边 (u,v)(u, v),残余容量 cf(u,v)=c(u,v)f(u,v)c_f(u,v) = c(u,v) - f(u,v)——还能再推多少
  • 反向残余边 (v,u)(v, u),残余容量 cf(v,u)=f(u,v)c_f(v,u) = f(u,v)——能撤回多少

如果残余容量为 0,对应的残余边不存在。

残余图:正向边与反向边原始网络uv7/10容量 10, 已用 7构建残余图残余图 (Residual Graph)uv37正向残余 = c(e) - f(e) (还能推多少)反向残余 = f(e) (能撤回多少)

直觉:正向边说”管道还有富余容量”,反向边说”你可以把已经推过去的流撤回来”。反向边是 Ford-Fulkerson 方法最关键的设计——它允许算法”反悔”之前的决策。

增广路(Augmenting Path)

在残余图 GfG_f 中,从 sstt 的一条路径称为增广路(augmenting path)。沿这条路径,我们可以推送额外的流量,推送量等于路径上最小的残余容量(瓶颈,bottleneck)。

在残余图中找到增广路增广路 (Augmenting Path) — 残余图中 s→t 的路径32541433769sABCDt增广路 s→A→C→t,瓶颈 = min(3, 5, 4) = 3,可增加 3 单位流

Ford-Fulkerson 方法

Ford-Fulkerson(1956)的方法非常简单:

Ford-Fulkerson(G, s, t):
    初始化 f(e) = 0 for all e
    while 残余图 G_f 中存在 s→t 增广路 p:
        bottleneck = 路径 p 上最小的残余容量
        沿 p 更新流量(正向边 +bottleneck,反向边 -bottleneck)
    return f

每次找到增广路并推送流量后,残余图发生变化,可能出现新的增广路。当残余图中不再存在 sts \to t 路径时,当前流就是最大流。

增广路定理(Ford-Fulkerson 定理):以下三个条件等价:

  1. ff 是最大流
  2. 残余图 GfG_f 中不存在 sts \to t 增广路
  3. 存在一个割 (S,T)(S, T) 使得 f=c(S,T)|f| = c(S, T)

这三者的等价性就是最大流最小割定理的完整版本。

为什么需要反向边?

这是初学者最容易困惑的地方:为什么残余图需要反向边?不能只用正向边吗?

没有反向边会卡住为什么需要反向边?第一轮贪心选了 s→A→B→t(流量 1),之后的残余图:无反向边 — 卡住了01010sABt最大流 = 1(错!正确答案是 2)有反向边 — 可以“反悔”010101sABt增广 s→B→A→t,总流 = 2 ✓反向边 B→A(橙色虚线)允许“撤回”之前 A→B 的流量结果等价于两条不相交的路径:s→A→t 和 s→B→t

上图展示了一个经典反例。图有 4 个节点,所有边容量为 1,最大流应该是 2(两条不相交路径 sAts \to A \to tsBts \to B \to t)。但如果第一轮贪心选了 sABts \to A \to B \to t(流量 1),没有反向边的话就卡住了——sAs \to ABtB \to t 都已经满了,而 sBs \to BAtA \to t 不连通。

有了反向边 BAB \to A(残余容量 = 1,允许撤回 ABA \to B 的流量),第二轮可以找到增广路 sBAts \to B \to A \to t。结果等价于两条不相交路径,总流 = 2。

反向边的本质是允许”反悔”——撤回之前不够好的流量分配,重新分配到更好的路径上。

Ford-Fulkerson 的局限

Ford-Fulkerson 只说了”找一条增广路”,没有规定怎么找。如果用 DFS 随意找,在容量为无理数的情况下算法可能不终止(Zwick, 1995)。即使容量为整数,如果用 DFS 找增广路,最坏情况下需要 O(f)O(|f^*|) 次增广(ff^* 是最大流值),每次 O(E)O(E),总时间 O(Ef)O(E \cdot |f^*|)——当最大流值很大时极其低效。

关键改进:规定增广路的选择策略

Edmonds-Karp 算法

Edmonds-Karp(1972)的改进非常简洁:用 BFS 找增广路(即找残余图中从 sstt最短增广路,以边数衡量)。

这个简单的改变带来了巨大的复杂度提升:

Edmonds-Karp(G, s, t):
    初始化 f(e) = 0 for all e
    while true:
        用 BFS 在残余图 G_f 中找 s→t 最短路径 p
        if p 不存在: break
        bottleneck = min{c_f(e) : e ∈ p}
        沿 p 更新流量
    return f

为什么 BFS 更好?

关键引理(Edmonds & Karp, 1972):在 Edmonds-Karp 算法中,残余图中 ss 到任意节点 vv 的 BFS 距离 d(s,v)d(s, v) 随着增广单调不减。

这意味着增广路的长度只会越来越长(不会变短)。由于最短路径长度最多为 V1|V| - 1,每个长度级别最多进行 O(E)O(E) 次增广(每次增广至少让一条边饱和从分层图中消失),所以总增广次数最多 O(VE)O(VE)。每次 BFS 花费 O(E)O(E),总时间为:

O(VE2)O(VE^2)

数值走查

下面的交互走查展示 Edmonds-Karp 在一个 6 节点网络上的完整执行过程。注意观察:

  • 每一轮 BFS 如何找到增广路
  • 推送流量后残余图如何变化
  • 最终没有增广路时算法终止
网络流走查 (Edmonds-Karp)总流量: 0
0/100/80/70/40/100/90/30/12sABCDt未使用有流量增广路已饱和
残余容量: sA:10sB:8AC:7AB:4BD:10Ct:9CD:3Dt:12
步骤 1/8: 初始状态:所有边流量为 0。
1 / 8

走查的关键观察:

  • 每轮增广都沿 BFS 找到的最短路径推送流量
  • 残余容量在每轮后更新——某些边饱和(变为 0),同时反向残余容量增加
  • 当残余图中 ss 无法到达 tt 时,算法终止——此时的流即为最大流

Dinic 算法

Dinic(1970)在 Edmonds-Karp 的基础上更进一步:不是每次只找一条增广路,而是一次性找到残余图中的所有最短增广路

核心思想:分层图 + 阻塞流

  1. BFS 分层:从 ss 出发在残余图上做 BFS,得到每个节点的距离 d[v]d[v]。构建分层图(layered graph):只保留满足 d[v]=d[u]+1d[v] = d[u] + 1 的残余边 (u,v)(u, v)
  2. 找阻塞流:在分层图上用 DFS 找阻塞流(blocking flow)——一个流,使得分层图中每条 sts \to t 路径上至少有一条边被饱和。
  3. 更新残余图:把阻塞流加到当前总流上,重新 BFS 分层,重复。
Dinic 算法的分层图与阻塞流Dinic 分层图 (Layered Graph)s d=0 d=1 d=2t d=358436752sABCDt非分层边 (不在分层图中)分层图只保留"向前走一层"的边 (d[v] = d[u] + 1)在分层图上找阻塞流(Blocking Flow):每条 s→t 路径至少有一条饱和边

为什么更快?

关键定理:每次找到阻塞流并更新后,分层图中 sstt 的距离严格增加至少 1。

由于 sstt 的距离最多为 V1|V| - 1,所以最多进行 O(V)O(V) 轮分层。每轮中:

  • BFS 分层:O(E)O(E)
  • 找阻塞流(DFS + 删边):O(VE)O(VE)

总时间:

O(V2E)O(V^2 E)

对比 Edmonds-Karp 的 O(VE2)O(VE^2),Dinic 在稠密图上更快(V2E<VE2V^2 E < VE^2V<EV < E 时)。

Dinic 在单位容量图和二部图上的加速

Dinic 算法有两个重要的特殊情况加速:

  • 单位容量图(所有边容量为 1):O(EV)O(E\sqrt{V})。因为每轮阻塞流用时 O(E)O(E),且距离增长更快。
  • 二部图匹配(本质是单位容量网络流):O(EV)O(E\sqrt{V})——这就是 Hopcroft-Karp 匹配算法的本质(Art. 13 将展开讨论)。

最小费用最大流

标准最大流问题只关心流量。但如果每条边不仅有容量 c(e)c(e),还有单位费用 w(e)w(e)(推一个单位的流经过这条边花费 w(e)w(e)),怎么办?

最小费用最大流(Minimum-Cost Maximum Flow, MCMF)问题:在所有最大流中,找一个总费用最小的。

mineEw(e)f(e)s.t.f=f(最大流值)\min \sum_{e \in E} w(e) \cdot f(e) \quad \text{s.t.} \quad |f| = |f^*| \text{(最大流值)}

核心方法:把 Edmonds-Karp 中的 BFS 替换为 SPFA(或 Bellman-Ford),每次找最短费用增广路(即残余图中以费用为权重的最短路径)。

由于边费用可以为负(反向边费用 = w(e)-w(e)),需要能处理负权边的最短路算法——这就是 Bellman-Ford 的用武之地。

MCMF 的时间复杂度为 O(V2E2)O(V^2 E^2)(最坏情况),但在实际应用中通常远快于此。

复杂度对比

算法时间复杂度(最坏)增广策略特点
Ford-Fulkerson (DFS)O(Ef)O(E \cdot \lvert f^* \rvert)任意 DFS可能不终止(无理数容量)
Edmonds-KarpO(VE2)O(VE^2)BFS 最短路一定终止,简单实用
DinicO(V2E)O(V^2 E)分层图 + 阻塞流实践中最快之一
Dinic (单位容量)O(EV)O(E\sqrt{V})同上匹配的理论基础
MCMF (SPFA)O(V2E2)O(V^2 E^2)最短费用路同时优化流量和费用
四种网络流算法对比网络流算法演进Ford-FulkersonDFS/任意路径O(E|f*|)可能不终止Edmonds-KarpBFS 最短增广路O(VE²)一定终止Dinic分层图+阻塞流O(V²E)实践最快之一最小费用最大流SPFA/Bellman-FordO(V²E²)优化总费用

选型建议:

  • 一般最大流:Dinic 是首选(代码量适中,速度快)
  • 简单实现:Edmonds-Karp(用标准 BFS)
  • 有费用约束:MCMF
  • 二部匹配:Dinic 或 Hopcroft-Karp(Dinic 在单位容量图上的特例)

应用场景

交通网络容量分析

一个城市的道路网络中,每条路有一个通行能力(车辆/小时)。从某个区域到另一个区域的最大通行能力就是一个最大流问题。交通规划师用最大流分析找出”瓶颈道路”(对应最小割中的边),针对性地扩容。

供应链优化

供应链中,工厂(源)通过仓库网络向零售店(汇)发货。每段物流通道有容量限制(卡车数/天)。最大流告诉你系统的最大吞吐量,最小割告诉你瓶颈在哪条物流线路上。加权版本(最小费用最大流)还能同时优化运输成本。

图像分割

计算机视觉中,图割(graph cut)是经典的图像分割方法(Boykov & Kolmogorov, 2004)。把图像建模为图:

  • 每个像素是一个节点
  • 相邻像素之间有边,权重反映颜色/亮度相似度(越相似,权重越大——切断成本高)
  • 添加源 ss(代表”前景”)和汇 tt(代表”背景”),ss 连到可能是前景的像素,tt 连到可能是背景的像素

最小 ss-tt 割把像素分成前景和背景两组。被切断的边权重之和最小,意味着分割线优先沿着颜色差异大的地方走——这恰好是自然的物体边界。

最小割用于图像分割图像分割 — s-t 割的应用s前景t背景最小割S 侧 (前景)T 侧 (背景)每个像素是节点,相邻像素间的边权 = 颜色差异,s/t 连接先验前景/背景种子

这个方法在医学影像分析(器官分割)、遥感图像处理等领域广泛应用。Boykov 和 Kolmogorov 在 2004 年的 benchmark 论文中系统比较了多种最大流算法在图像分割任务上的性能——对于图像分割产生的特殊图结构,专门优化的实现比通用算法快一个数量级。

二部匹配

二部匹配(bipartite matching)是网络流的一个重要特例。给定二部图 G=(LR,E)G = (L \cup R, E),添加源 ss 连到所有左侧节点(容量 1),汇 tt 连到所有右侧节点(容量 1),原始边容量为 1。在这个网络上求最大流,就得到了最大匹配数。

Dinic 在单位容量图上的 O(EV)O(E\sqrt{V}) 复杂度,就是 Hopcroft-Karp 二部匹配算法的本质。下一篇 Art. 13: 匹配 将详细展开匹配问题。

🔁 迭代机器视角Ford-Fulkerson / Edmonds-KarpOPT: 优化目标

Graph残余图(动态变化)
State[v]残余容量 c_f(u,v)
SELECTBFS 找增广路(Edmonds-Karp)
COMBINE沿增广路更新残余容量
重入到不动点(无增广路)
TERMINATE无增广路
求解方法NESTED

嵌套结构:外层迭代找增广路 × 内层 BFS/DFS 搜索路径

→ 框架详解:Art. 0A — 图上的通用迭代机器

🔁 迭代机器视角Push-RelabelOPT: 优化目标

Graph残余图
State[v]excess(v) + height(v)
SELECT活跃节点(excess > 0)
COMBINEpush: 沿可行边推送流量 / relabel: 提升高度
重入到不动点(无活跃节点)
TERMINATE无活跃节点(除 s, t)
求解方法FI

→ 框架详解:Art. 0A — 图上的通用迭代机器

总结

本文建立了图上”流量”的完整工具箱:

  • 流网络的基本框架:容量约束 + 流量守恒
  • 最大流最小割定理:对偶性的力量——最大流 = 最小割,一个优化问题等价于一个组合问题
  • 残余图和增广路:Ford-Fulkerson 方法的核心——在残余图中找增广路,推送流量,反向边允许”反悔”
  • Edmonds-Karp:BFS 找最短增广路,O(VE2)O(VE^2),简单可靠
  • Dinic:分层图 + 阻塞流,O(V2E)O(V^2 E),实践最快
  • MCMF:用最短费用路替代 BFS,同时优化流量和费用

从算法范式的角度:

  • 增广路范式贯穿始终——每次在残余图中找到改善的路径,沿路径推进
  • 对偶性(最大流 = 最小割)是理论核心——它不仅给出了终止条件,还揭示了最优解的结构

网络流是 Part 3”优”的基石。下一个自然的问题是:如何在图中找到最大匹配? 下一篇 Art. 13: 匹配 将展示,二部匹配就是单位容量网络流的特例——而一般匹配需要更精巧的工具。

实现指引

算法函数/方法
最大流 (Edmonds-Karp)NetworkXnx.maximum_flow(G, s, t, flow_func=edmonds_karp)
最大流 (Dinic/默认)NetworkXnx.maximum_flow(G, s, t)
最大流值NetworkXnx.maximum_flow_value(G, s, t)
最小割NetworkXnx.minimum_cut(G, s, t)
最小费用最大流NetworkXnx.max_flow_min_cost(G, s, t)
最小费用流NetworkXnx.min_cost_flow(G)
最大流scipyscipy.sparse.csgraph.maximum_flow(graph, source, sink)
最大流igraphg.maxflow(source, target, capacity)
最小割igraphg.mincut(source, target, capacity)