初识Dinic

本文深入探讨了网络流和最大流算法,一种用于解决复杂问题的高效算法。介绍了网络流的基本概念,包括源点、汇点、流量和容量限制,并详细解释了最大流问题。文章还介绍了两种算法:暴力算法和Dinic算法,通过实例代码展示了算法的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于网络流和最大流

这是一种非常神奇 (毒瘤) 的算法,可以用来解决很多稀奇古怪的问题和限制条件(当然建模是难点),并跑出各种不可思议的时间复杂度。
在网络流中,一个有向图有且仅有一个源点 S S S和一个汇点 T T T,每条有向边可以看成一根有流量为 f f f的水流过的容量上限为 c c c的定向水管,其中 0 ≤ f ≤ c 0≤f≤c 0fc
S S S T T T外每个点的流入量都要 = = =每个点的流出量, S S S的净流出量 = T =T =T的净流入量。
而最普通的最大流问题即是询问一张网络图中从 S S S T T T能流出的最大流量
以下内容参考自 N i r o B C NiroBC NiroBC大神

关于暴力算法

每次找出一条从源点到汇点的由未流满的边组成的路径,使得该路径每条边流量增加同一个数。这样的路径叫增广路。
不断找增广路,直到找不出增广路。
需要注意的是,每发现一条增广路,所有这条增广路使用过的边的反向边的剩余容量需要加上流过的流量。因为之后的增广路可能会反着走这条边。

关于奇妙的 D i n i c Dinic Dinic

做法

重复以下过程:
1. 1. 1. b f s bfs bfs 对每个点 u u u 求出,从 S S S开始只经过未流满的边走到该点,至少需要走过的边数,记作 d e p u dep_u depu
2. 2. 2. 只经过由深度为 d e p dep dep的点走向深度为 d e p + 1 dep+1 dep+1的边,找出这种情况下所有的增广路。

Dinic 的时间复杂度

首先, b f s bfs bfs 最多进行 O ( n ) O(n) O(n) 次,因为每一轮 b f s bfs bfs 后的 d e p T dep_T depT 都是严格单调递增的。
每一次 d f s dfs dfs 后,对于同一张分层图,我们可以用最多 O ( n m ) O(nm) O(nm) 的时间找出所有增广路。
所以总时间复杂度 O ( n 2 e ) O(n^2e) O(n2e)

代码实现
#include <queue>
#include <cstdio>
using namespace std;
const int maxn = 10005;
const int maxe = 100005;
const int oo = 2000000000;
int n;
int edge_cnt;
struct edge {int nxt, to, val;} E[maxe << 1];
int fir[maxn];
int s, t;
int dep[maxn], fl[maxn];
int min(int x, int y) {return x < y ? x : y;}
int read() {
    char ch = getchar(); bool f = 1;
    while(ch < '0' || ch > '9') f &= ch != '-', ch = getchar();
    int res = 0;
    while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
    return f ? res : -res;
}
void clear_edge() {
    edge_cnt = 1;
    for(int i = 1; i <= n; i++) fir[i] = 0;
}
void add_edge(int u, int v, int val) {
    E[++edge_cnt] = (edge) {fir[u], v, val};
    fir[u] = edge_cnt;
}
bool bfs() {
    for(int i = 1; i <= n; i++) dep[i] = oo;
    queue<int> q;
    q.push(s);
    dep[s] = 0;
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int e = fir[u]; e; e = E[e].nxt) {
            int v = E[e].to;
            if(!E[e].val || dep[v] < oo) continue;
            dep[v] = dep[u] + 1;
            q.push(v);
        }
    }
    return dep[t] < oo;
}
int dfs(int u, int f) {
    if(u == t) return f;
    fl[u] = 0;
    for(int e = fir[u]; e; e = E[e].nxt) {
        int v = E[e].to;
        if(!E[e].val || dep[v] != dep[u] + 1) continue;
        int got = dfs(v, min(f, E[e].val));
        if(!got) continue;
        f -= got;
        fl[u] += got;
        E[e].val -= got;
        E[e ^ 1].val += got;
        if(!f) return fl[u];
    }
    return fl[u];
}
int Dinic() {
    int res = 0;
    while(bfs()) res += dfs(s, oo);
    return res;
}
int main() {
    n = read();
    int m = read();
    s = read(), t = read();
    clear_edge();
    while(m--) {
        int u = read(), v = read(), val = read();
        add_edge(u, v, val), add_edge(v, u, 0);
    }
    printf("%d\n", Dinic());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值