【学习笔记】基础图论:网络流(最大流篇)

这应该算是复习?所以写的比较浅

简介

网络就是一张图 \(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);
    }
}
posted @ 2024-08-19 10:23  Vsinger_洛天依  阅读(65)  评论(0编辑  收藏  举报