一、网络流问题是什么?

想象一下城市里的水管网络:水厂是源头,你家是终点,中间有各种管道连接。每条管道有粗细(容量),问:从水厂到你家,最多能流多少水?这就是最大流问题的现实版。

在计算机中,我们把这类问题抽象成有向图

  • 节点代表"中转站"(如水管连接点)
  • 边代表"管道",有方向(水流方向)和容量(最大流量)

核心目标:找到从起点(源点)到终点(汇点)的最大流量。

二、Edmonds-Karp算法:用BFS找最短路径

Edmonds-Karp是Ford-Fulkerson算法的具体实现,核心思想:每次用BFS找最短的增广路径,然后更新剩余容量。

关键步骤:

  1. 初始化:所有边流量设为0
  2. 找路径:用BFS找一条从源点到汇点的路径(边剩余容量>0)
  3. 算流量:路径上最小的剩余容量就是本次能增加的流量
  4. 更新图:正向边减流量,反向边加流量(反向边用于"反悔")
  5. 重复:直到找不到新的增广路径

Python示例(技术栈:Python)

from collections import deque

def edmonds_karp(graph, source, sink):
    n = len(graph)
    flow = 0
    parent = [-1] * n
    
    # BFS找增广路径
    def bfs():
        visited = [False] * n
        queue = deque([source])
        visited[source] = True
        while queue:
            u = queue.popleft()
            for v, capacity in enumerate(graph[u]):
                if not visited[v] and capacity > 0:
                    visited[v] = True
                    parent[v] = u
                    if v == sink:
                        return True
                    queue.append(v)
        return False
    
    # 每次找到路径后更新流量
    while bfs():
        path_flow = float('inf')
        v = sink
        # 计算路径最小剩余容量
        while v != source:
            u = parent[v]
            path_flow = min(path_flow, graph[u][v])
            v = u
        # 更新流量和残余图
        v = sink
        while v != source:
            u = parent[v]
            graph[u][v] -= path_flow  # 正向边减流量
            graph[v][u] += path_flow  # 反向边加流量(用于回退)
            v = u
        flow += path_flow
    return flow

# 示例图:邻接矩阵表示容量
graph = [
    [0, 3, 0, 3, 0, 0, 0],  # 节点0(源点)
    [0, 0, 4, 0, 0, 0, 0],  # 节点1
    [0, 0, 0, 1, 2, 0, 0],  # 节点2
    [0, 0, 0, 0, 2, 6, 0],  # 节点3
    [0, 0, 0, 0, 0, 0, 3],  # 节点4
    [0, 0, 0, 0, 0, 0, 3],  # 节点5
    [0, 0, 0, 0, 0, 0, 0]   # 节点6(汇点)
]
print("最大流量:", edmonds_karp(graph, 0, 6))  # 输出:5

优缺点:

  • 优点:简单易实现,时间复杂度O(V·E²)(V是节点数,E是边数)
  • 缺点:BFS每次只找最短路径,可能不是最优选择

三、Dinic算法:分层图+DFS多路增广

Dinic算法优化思路:先对图分层,再用DFS一次性找多条增广路径

关键改进:

  1. 分层图(BFS):计算每个节点到源点的最短距离(层数)
  2. 阻塞流(DFS):在分层图上,用DFS找到所有可能的增广路径
  3. 重复:直到无法分层(汇点不可达)

Python示例(技术栈:Python)

from collections import deque

def dinic(graph, source, sink):
    n = len(graph)
    level = [0] * n
    flow = 0
    
    # BFS构建分层图
    def bfs():
        nonlocal level
        level = [-1] * n
        queue = deque([source])
        level[source] = 0
        while queue:
            u = queue.popleft()
            for v, capacity in enumerate(graph[u]):
                if level[v] == -1 and capacity > 0:
                    level[v] = level[u] + 1
                    if v == sink:
                        return True
                    queue.append(v)
        return False
    
    # DFS找阻塞流
    def dfs(u, incoming_flow):
        if u == sink:
            return incoming_flow
        for v in range(n):
            if level[v] == level[u] + 1 and graph[u][v] > 0:
                pushed_flow = dfs(v, min(incoming_flow, graph[u][v]))
                if pushed_flow > 0:
                    graph[u][v] -= pushed_flow
                    graph[v][u] += pushed_flow
                    return pushed_flow
        return 0
    
    while bfs():
        while True:
            f = dfs(source, float('inf'))
            if f == 0:
                break
            flow += f
    return flow

# 使用同样的示例图
graph = [
    [0, 3, 0, 3, 0, 0, 0],
    [0, 0, 4, 0, 0, 0, 0],
    [0, 0, 0, 1, 2, 0, 0],
    [0, 0, 0, 0, 2, 6, 0],
    [0, 0, 0, 0, 0, 0, 3],
    [0, 0, 0, 0, 0, 0, 3],
    [0, 0, 0, 0, 0, 0, 0]
]
print("最大流量:", dinic(graph, 0, 6))  # 输出:5

优缺点:

  • 优点:时间复杂度O(V²·E),实际性能通常优于Edmonds-Karp
  • 缺点:实现稍复杂,DFS递归可能栈溢出(可改迭代DFS)

四、应用场景与选型建议

常见应用:

  1. 交通规划:计算路网最大通行能力
  2. 电力分配:电网负载均衡
  3. 数据流:网络带宽分配(如CDN流量调度)
  4. 匹配问题:如求职平台匹配岗位与求职者

如何选择算法?

场景 推荐算法 理由
小规模图(V<100) Edmonds-Karp 实现简单,代码少
大规模稀疏图 Dinic 分层图减少DFS搜索次数
需要快速原型开发 Edmonds-Karp 调试方便

注意事项:

  1. 反向边:必须正确处理反向边,否则算法失效
  2. 容量初始化:确保反向边初始容量为0
  3. 整数流量:如果容量是浮点数,需注意精度问题

五、总结

  • Edmonds-Karp像"谨慎的快递员",每次只走最短路线,适合小规模问题。
  • Dinic像"高效的物流系统",先规划区域(分层),再批量送货(多路增广)。

实际开发中,如果对性能要求不高,优先用Edmonds-Karp;面对复杂网络时,Dinic往往是更好的选择。