Loading

Dinic算法

Dinic 何尝又不是EK的一种变形呢?

以下是Dinic算法的C++实现,包含详细的代码注释,帮助理解算法的每个步骤。

#include <iostream>
#include <vector>
#include <queue>
#include <climits>

using namespace std;

struct Edge {
    int to;     // 边的终点
    int rev;    // 反向边在邻接表中的位置
    int flow;   // 当前流量
    int cap;    // 容量
};

class Dinic {
private:
    int n;                          // 节点数量
    vector<vector<Edge>> graph;     // 邻接表表示的图
    vector<int> level;              // 节点的层级(用于BFS分层)
    vector<int> ptr;                // 当前弧优化指针(用于DFS)

    // BFS分层:构建层次图,返回是否能到达汇点
    bool bfs(int s, int t) {
        fill(level.begin(), level.end(), -1);
        queue<int> q;
        
        level[s] = 0;   // 源点层级为0
        q.push(s);
        
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            
            // 遍历所有邻边
            for (Edge &e : graph[u]) {
                // 如果该边还有剩余容量,并且目标节点未被分层
                if (e.flow < e.cap && level[e.to] == -1) {
                    level[e.to] = level[u] + 1;  // 层级+1
                    q.push(e.to);
                }
            }
        }
        
        return level[t] != -1;  // 返回是否能到达汇点
    }

    // DFS寻找阻塞流(多路增广)
    int dfs(int u, int t, int flow) {
        if (u == t || flow == 0) {
            return flow;  // 到达汇点或没有剩余流量
        }
        
        // 使用当前弧优化,避免重复检查已经处理过的边
        for (int &i = ptr[u]; i < graph[u].size(); i++) {
            Edge &e = graph[u][i];
            
            // 只向更高层级的节点推进
            if (level[e.to] == level[u] + 1) {
                // 递归寻找增广路径
                int pushed = dfs(e.to, t, min(flow, e.cap - e.flow));
                
                if (pushed > 0) {
                    // 更新边的流量
                    e.flow += pushed;
                    // 更新反向边的流量
                    graph[e.to][e.rev].flow -= pushed;
                    return pushed;
                }
            }
        }
        
        return 0;  // 没有找到增广路径
    }

public:
    Dinic(int n) : n(n) {
        graph.resize(n);
        level.resize(n);
        ptr.resize(n);
    }

    // 添加有向边(同时自动添加反向边)
    void addEdge(int from, int to, int cap) {
        // 正向边
        graph[from].push_back({to, (int)graph[to].size(), 0, cap});
        // 反向边(初始容量为0)
        graph[to].push_back({from, (int)graph[from].size() - 1, 0, 0});
    }

    // 计算最大流
    int maxFlow(int s, int t) {
        int total_flow = 0;
        
        // 只要还能分层(即还能找到增广路径)
        while (bfs(s, t)) {
            fill(ptr.begin(), ptr.end(), 0);  // 重置当前弧指针
            
            // 不断寻找阻塞流
            while (int pushed = dfs(s, t, INT_MAX)) {
                total_flow += pushed;
            }
        }
        
        return total_flow;
    }
};

int main() {
    // 示例:解决一个简单的网络流问题
    Dinic dinic(6);  // 创建6个节点的图
    
    // 添加边(起点,终点,容量)
    dinic.addEdge(0, 1, 10);
    dinic.addEdge(0, 2, 10);
    dinic.addEdge(1, 2, 2);
    dinic.addEdge(1, 3, 4);
    dinic.addEdge(1, 4, 8);
    dinic.addEdge(2, 4, 9);
    dinic.addEdge(3, 5, 10);
    dinic.addEdge(4, 3, 6);
    dinic.addEdge(4, 5, 10);
    
    // 计算从节点0(源点)到节点5(汇点)的最大流
    cout << "Maximum flow: " << dinic.maxFlow(0, 5) << endl;
    
    return 0;
}

关键部分详细解释

1. 数据结构

  • Edge结构体存储边的信息:
    • to: 边的终点
    • rev: 反向边在邻接表中的位置(用于快速查找)
    • flow: 当前流量
    • cap: 边的容量

2. BFS分层 (bfs函数)

  • 为每个节点分配层级,形成分层图
  • 只考虑有剩余容量的边(e.flow < e.cap
  • 返回是否能到达汇点(判断算法是否继续)

3. DFS寻找阻塞流 (dfs函数)

  • 使用当前弧优化(ptr数组)避免重复检查边
  • 只向更高层级的节点推进(level[e.to] == level[u] + 1
  • 递归寻找增广路径并更新流量

4. 最大流计算 (maxFlow函数)

  • 不断执行BFS分层和DFS阻塞流寻找
  • 直到无法再分层(没有增广路径)为止

5. 当前弧优化

  • ptr数组记录每个节点已经检查过的边
  • 避免在多次DFS中重复检查不可能再增广的边

复杂度分析

  • 时间复杂度:O(V²E)
  • 空间复杂度:O(V+E)

这个实现包含了Dinic算法的所有关键优化,包括分层图、阻塞流和当前弧优化,是解决网络流问题的高效实现。

posted @ 2025-07-17 11:23  Venlacy  阅读(64)  评论(0)    收藏  举报