网络流
dinic算法
- 存图技巧:反向边是灵魂
不能只用 pair 存正向边。因为网络流在扣除正向容量时,必须同时增加反向边的容量以提供反悔机会。
标准写法: 用 struct Edge 存边,包含 to(终点)、cap(剩余容量)和 rev(反向边在对方邻接表里的下标)。
初始状态: 正向边容量为 \(c\),反向边初始容量必须为 \(0\)。
- BFS 分层器(构建层次图)作用: 给每个点标上距离起点 \(S\) 的步数(level)。意义: 规定水只能从层数低的地方流向层数高的地方(level[v] == level[u] + 1),彻底杜绝水流在图里绕圈子的死循环。
- 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;
}