【学习笔记】基础图论:网络流(最大流篇)
这应该算是复习?所以写的比较浅
简介
网络就是一张图 \(G = (V,E)\),除了有源点和汇点,容量以外和普通的图没有区别
-
容量:每条边都有一个容量
-
源点:出发点
-
汇点:结束点
-
流:一个合法解称作一个流,也就是一条可以从源点到汇点的一条合法路径。
-
流量:每条边各自被经过的次数称作其流量,最终收集的总数为整个流的流量。
网络流会满足以下两个性质
-
容量限制:每条边的流量不超过其容量(水管会爆的)。
-
流量平衡:对于除源点和汇点以外的点来说,其流入量一定等于流出量。
网络流一般可以分为以下几个问题:最大流,最小割,费用流
最大流
我们希望去最大化整张网络上的流量 \(|f|\),这个问题就是最大流
在求最大流时我们一般会使用以下方法
-
在图上找到一条增广路(从源点到汇点的路径)
-
去掉增广路上的残量最小值 \(v\)
-
将答案加上 \(v\)
-
将增广路上所有边的残量减去 \(v\),反向边的残量加上 \(v\)
这个方法称为 Ford–Fulkerson 增广
Ford-Fulkerson 增广 是不会死循环的,因为每次增广都会导致流量增加,而流量存在一个最大值
没有指定走的方向的情况下随便乱走是肯定不够优的,因此我们有两种算法可以考虑
Edmonds–Karp 算法
在最自然的情况下一定是考虑使用 BFS
来解的,
-
如果在 \(G_f\) 上我们可以从 \(s\) 出发
BFS
到 \(t\),则我们找到了新的增广路 -
对于增广路 \(p\),我们计算出 \(p\) 经过的边的剩余容量的最小值 \(\Delta = \min_{(u, v) \in p} c_f(u, v)\)。
我们给 \(p\) 上的每条边都加上 \(\Delta\) 流量,并给它们的反向边都退掉 \(\Delta\) 流量,令最大流增加了 \(\Delta\)。
-
因为我们修改了流量,所以我们得到新的 \(G_f\),我们在新的 \(G_f\) 上重复上述过程,直至增广路不存在,则流量不再增加。
上界复杂度 \(O(|V||E|^2)\),虽然我们一般跑不到 EK
的上界,但是复杂度还是不够
因此考虑优化
Dinic 算法
考虑在增广前先对 \(G_f\) 做 BFS
分层,即根据结点 \(u\) 到源点 \(s\) 的距离 \(d(u)\) 把结点分成若干层。
令经过 \(u\) 的流量只能流向下一层的结点 \(v\),即删除 \(u\) 向层数标号相等或更小的结点的出边,我们称 \(G_f\) 剩下的部分为层次图
如果我们在层次图 \(G_L\) 上找到一个最大的增广流 \(f_b\),使得仅在 \(G_L\) 上是不可能找出更大的增广流的,则我们称 \(f_b\) 是 \(G_L\) 的阻塞流
我们可以用以下步骤来设计 Dinic 算法:
-
在 \(G_f\) 上
BFS
出层次图 \(G_L\) -
在 \(G_L\) 上
DFS
出阻塞流 \(f_b\) -
将 \(f_b\) 并到原先的流 \(f\) 中,即 \(f \leftarrow f + f_b\)
-
重复以上过程直到不存在从 \(s\) 到 \(t\) 的路径
此时的 \(f\) 就是最大流,复杂度为 \(O(|V|^2E)\)
点击查看代码
inline bool bfs() {
queue<int> q;
memset(dep, 0, sizeof(int) * (n + 1));
dep[S] = 1;
q.push(S);
while (q.size()) {
int u = q.front();
q.pop();
for (int i = fir[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if ((!dep[v]) && (e[i].cap > e[i].flow)) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[T];
}
inline int dfs(int u, int flow){
if ((u == T) || (!flow))
return flow;
int ret = 0;
for (int& i = cur[u]; ~i; i = e[i].nxt) {
int v = e[i].v, d;
if ((dep[v] == dep[u] + 1) && (d = dfs(v, min(flow - ret, e[i].cap - e[i].flow)))) {
ret += d;
e[i].flow += d;
e[i ^ 1].flow -= d;
if (ret == flow)
return ret;
}
}
return ret;
}
inline void dinic() {
while(bfs()){
memcpy(cur, fir, sizeof(int) * (n + 1));
maxflow += dfs(S, INF);
}
}