古典叙事 · 技术札记

序章

山河有卷
人间有声

写代码,也写长风、旧城与未熄的灯。
愿每一篇随笔,都有自己的山水与回声。
阅览随笔

网络流

网络流

dinic算法

  1. 存图技巧:反向边是灵魂
    不能只用 pair 存正向边。因为网络流在扣除正向容量时,必须同时增加反向边的容量以提供反悔机会。
    标准写法: 用 struct Edge 存边,包含 to(终点)、cap(剩余容量)和 rev(反向边在对方邻接表里的下标)。
    初始状态: 正向边容量为 \(c\),反向边初始容量必须为 \(0\)
  2. BFS 分层器(构建层次图)作用: 给每个点标上距离起点 \(S\) 的步数(level)。意义: 规定水只能从层数低的地方流向层数高的地方(level[v] == level[u] + 1),彻底杜绝水流在图里绕圈子的死循环。
  3. DFS 多路增广(贪心榨取)在 BFS 建好的层次图上,DFS 负责一路向前冲,寻找能到达终点 \(T\) 的路径(增广路)。当前弧优化(关键生死线): 必须用 iter[u] 数组记录当前点 \(u\) 已经遍历到了哪条边。一条边如果被榨干了或者走进了死胡同,下次再搜到 \(u\) 时直接跳过。不加这个优化,Dinic 复杂度是假的,必定 TLE。

模板题代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const i64 INF = 1e18; // 无穷大流量

i64 n, m, s, t;

struct Edge {
    i64 to;   // 指向的点
    i64 cap;  // 剩余容量
    i64 rev;  // 反向边在 tr[to] 中的下标
};
vector<Edge> tr[3005];
i64 level[3005];
i64 iter[3005];

// 加边函数:同时加入正向边和反向边
void add_edge(i64 from, i64 to, i64 cap) {
    tr[from].push_back({to, cap, (i64)tr[to].size()});
    tr[to].push_back({from, 0, (i64)tr[from].size() - 1});
}

// BFS:建立层次图
bool bfs(int s1, int t1) {
    for(int i = 0; i <= t; i++) level[i] = -1;
    level[s1] = 0;
    queue<i64> q;
    q.push(s1);
    while(!q.empty()) {
        auto u = q.front();
        q.pop();
        for(auto& it : tr[u]) {
            if(it.cap > 0 && level[it.to] == -1) { // 只走有容量且没去过的点
                level[it.to] = level[u] + 1;
                q.push(it.to);
            }
        }
    }
    return level[t1] != -1; // 返回是否还能到达终点
}

// DFS:在层次图上寻找增广路
i64 dfs(i64 u, i64 v, i64 flow) {
    if(u == v) return flow; // 到达终点,返回榨取到的流量
    
    // 【当前弧优化】:注意这里是引用 &i
    for(i64 &i = iter[u]; i < tr[u].size(); i++) {
        Edge& e = tr[u][i];
        if(e.cap > 0 && level[e.to] == level[u] + 1) { // 严格按照层次流
            i64 pushed = dfs(e.to, v, min(flow, e.cap));
            if(pushed > 0) {
                e.cap -= pushed;                           // 正向减
                tr[e.to][e.rev].cap += pushed;             // 反向加(后悔药)
                return pushed;
            }
        }
    }
    return 0; // 死胡同
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> s >> t;
    for(int i = 1; i <= m; i++) {
        i64 x, y, z;
        cin >> x >> y >> z;
        add_edge(x, y, z);
    }
    
    i64 totalflow = 0;
    // 只要还能建出层次图,就不断压榨
    while(bfs(s, t)) {
        // 新的层次图,当前弧优化数组必须清零!
        for(int i = 0; i <= t; i++) iter[i] = 0; 
        
        i64 f;
        // 把当前层次图里能找的路全部找完,加到总流量里
        while((f = dfs(s, t, INF)) > 0) {
            totalflow += f;
        }
    }
    cout << totalflow << "\n";
    return 0;
}
posted @ 2026-08-30 15:34  Morphis‘  阅读(2)  评论(0)    收藏  举报