网络流学习笔记

关于什么是网络流

相当于我们有一张有向图 \(G=(V,E)\)。对于每条边,都有它的容量和流量,即 \(c(u,v),f(u,v)\)。同时给定 \(s\) 作为源点,\(t\) 作为汇点。对于一个函数 \(f(u,v)\) 我们称 \(u\) 流出了 \(f(u,v)\)\(v\) 汇入了 \(f(u,v)\)

  • 对于每个点(除 source 和 sink),不储存任何流,也就是每个点流出的总量等于汇入的总量。

  • \((u,v) \notin E,c(u,v)=0\)

  • \(f(u,v)\le c(u,v)\)

注意 \(f(u,v)\) 的值域为 \(Z\)

同时 \(s\) 点的流出和为整张图的流量。

最大流

我们需要找到整张图最大的流量。

我们先来介绍 FF 算法,其实,这是接下来所有算法的模型,也就是加下来这些算法的基础。

增广路:一条从 \(s\)\(t\) 的路径,对于路径上的每条边,\((u,v)\in P,f(u,v) < c(u,v)\),即每条边都在残量网络 \(G_{f}\) 里。

对于 FF 算法,想的就是,我们抽一条增广路增广。然后整个网络的流量和增广路上的流量增加。但是这样并不能保证一定最优,所以我们仿照反悔贪心,同时给所有增广路上的反边流量减少同样的值。这样我们在增广的时候也可以选取反边就相当于反悔了。这样做直到没有一条增广路,就可以得到答案。

这类算法时间复杂度上界是 \(O(mf)\) 的,\(f\)\(G\) 上的最大流,显然每次流量都会增加,每次最多遍历 \(m\) 条边。

接下来我们来学习 EK 算法,它的增广措施如下:

\(s\) BFS 到 \(t\),其中对于路径上的每条边增加 \((u,v) \in G_{f},\min\{c(u,v)-f(u,v)\}\)。同时所有反边减少 \((u,v) \in G_{f},\min\{c(u,v)-f(u,v)\}\),这样直到没有一条增广路,就是答案。

时间复杂度:\(O(nm^2)\)

想要更高明的增广?详见 Dinic 算法。

Dinic 算法会算出每个点的距离函数 \(D(u)\) 表示点到源点的最短距离。它仅会保留原图中 \(D(v)=D(u)+1\) 的边。

然后在建出的新图中,寻找最大的增广流(阻塞流),使得新图中上不可能找出更大的增广流。

这样的时间复杂度是 \(O(n^2m)\) 的。

下面给出实现:

// Problem: P3376 【模板】网络最大流
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3376
// Memory Limit: 512 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define int long long
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
const int N = 210, M = 1e4 + 10, INF = 0x3f3f3f3f3f3f3f3f;
int h[N], hh[N], ne[M], e[M], flow[M], lim[M], dep[N], idx, cnt, maxflow;
int n, m, S, T;

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
void init() {
    memset(h, -1, sizeof h);
    cnt = 0;
}
bool bfs() {
    queue<int> q;
    memset(dep, 0, sizeof dep);
    dep[S] = 1;
    q.push(S);
    while (q.size()) {
        auto iter = q.front();
        q.pop();
        for (int i = h[iter]; i != -1; i = ne[i]) {
            int j = e[i];
            if ((!dep[j]) && (flow[i] < lim[i])) {
                dep[j] = dep[iter] + 1;
                q.push(j);
            }
        }
    }
    return dep[T];
}
int dfs(int u, int fl) {
    if (u == T || !fl) return fl;
    int ret = 0;
    for (int &i = hh[u]; i != -1; i = ne[i]) {
        int j = e[i], d;
        if (dep[j] != dep[u] + 1) continue;
        if (!(d = dfs(j, min(fl - ret, lim[i] - flow[i])))) continue;
        ret += d;
        flow[i] += d;
        flow[(i ^ 1)] -= d;
        if(ret==fl) return ret;
    }
    return ret;
}
void dinic() {
    while (bfs()) {
        memcpy(hh, h, sizeof hh);
        maxflow += dfs(S, INF);
    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    init();
    cin >> n >> m >> S >> T;
    upp(i, 1, m) {
        int x, y, z;
        cin >> x >> y >> z;
        lim[idx] = z;
        add(x, y);
        lim[idx] = 0;
        add(y, x);
    }
    dinic();
    cout << maxflow << endl;
    return 0;
}
posted @ 2025-03-26 16:05  PM_pro  阅读(5)  评论(0)    收藏  举报