1. 程式人生 > 其它 >洛谷-P3376 【模板】網路最大流

洛谷-P3376 【模板】網路最大流

P3376 【模板】網路最大流

網路流

FF 演算法

直接用 dfs 求增廣路

時間複雜度 \(O(ef)\)\(e\) 為邊,\(f\) 為最大流

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll inf = 1e17 + 10;
int head[maxn], nex[maxn], tp = 2, to[maxn], vis[maxn];
ll vol[maxn];
int n, m, s, t;

void add(int u, int v, int w)
{
    to[tp] = v;
    vol[tp] = w;
    nex[tp] = head[u];
    head[u] = tp;
    tp++;
}

ll dfs(int now, ll flow)
{
    if(t == now)
        return flow;
    vis[now] = 1;
    for(int i=head[now]; i; i=nex[i])
    {
        int v = to[i];
        if(vol[i] <= 0 || vis[v] == 1) continue;
        ll f = dfs(v, min(flow, vol[i]));
        if(f != -1)
        {
            vol[i] -= f;
            vol[i ^ 1] += f;
            return f;
        }
    }
    return -1;
}

ll FF()
{
    ll ans = 0, now = 0;
    while((now = dfs(s, inf)) != -1)
    {
        ans += now;
        for(int i=0; i<=n; i++) vis[i] = 0;
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> s >> t;
    while(m--)
    {
        int l, r, w;
        cin >> l >> r >> w;
        add(l, r, w);
        add(r, l, 0);
    }
    cout << FF() << endl;
    return 0;
}

EK 演算法

改用 bfs 求增廣路

時間複雜度 \(O(ve^2)\)\(v\) 為點數,\(e\) 為邊數

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll inf = 1e17 + 10;
int nex[maxn], head[maxn], to[maxn], tp = 1, last[maxn];
ll vol[maxn], flow[maxn];
int n, m, s, t;

inline void add(int u, int v, int w)
{
    tp++;
    nex[tp] = head[u];
    head[u] = tp;
    vol[tp] = w;
    to[tp] = v;
}

bool bfs()
{
    queue<int>q;
    q.push(s);
    last[s] = 0;
    while(q.size())
    {
        int now = q.front();
        q.pop();
        if(now == t) break;
        for(int i=head[now]; i; i=nex[i])
        {
            int v = to[i];
            if(last[v] != -1 || vol[i] <= 0) continue;
            last[v] = i ^ 1;
            flow[v] = min(vol[i], flow[now]);
            q.push(v);
        }
    }
    return last[t] != -1;
}

ll EK()
{
    ll ans = 0;
    flow[s] = inf;
    for(int i=0; i<=n; i++) last[i] = -1;
    while(bfs())
    {
        for(int i=last[t]; i; i=last[to[i]])
        {
            vol[i] += flow[t];
            vol[i ^ 1] -= flow[t];
        }
        ans += flow[t];
        flow[s] = inf;
        for(int i=0; i<=n; i++) last[i] = -1;
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> s >> t;
    while(m--)
    {
        int l, r, w;
        cin >> l >> r >> w;
        add(l, r, w);
        add(r, l, 0);
    }
    cout << EK() << endl;
    return 0;
}

Dinic 演算法

用 bfs 分層之後,用 dfs 多次求該分層的所有增廣路

時間複雜度 \(O(v^2e)\)\(v\) 為點數,\(e\) 為邊數

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll inf = 1e17 + 10;
int head[maxn], nex[maxn], to[maxn], tp = 1, dep[maxn];
ll vol[maxn];
int n, m, s, t;

void add(int l, int r, int w)
{
    tp++;
    nex[tp] = head[l];
    vol[tp] = w;
    to[tp] = r;
    head[l] = tp;
}

bool bfs()
{
    queue<int>q;
    for(int i=0; i<=n; i++) dep[i] = -1;
    q.push(s);
    dep[s] = 0;
    while(q.size())
    {
        int now = q.front();
        q.pop();
        for(int i=head[now]; i; i=nex[i])
        {
            int u = to[i];
            if(dep[u] != -1 || vol[i] <= 0) continue;
            dep[u] = dep[now] + 1;
            q.push(u);
        }
    }
    return dep[t] != -1;
}

ll dfs(int now, ll flow)
{
    if(now == t)
        return flow;
    ll ans = 0;
    for(int i=head[now]; i && flow; i=nex[i])
    {
        int u = to[i];
        if(vol[i] <= 0 || dep[u] != dep[now] + 1) continue;
        ll f = dfs(u, min(flow, vol[i]));
        vol[i] -= f;
        vol[i ^ 1] += f;
        ans += f;
        flow -= f;
    }
    return ans;
}

ll dinic()
{
    ll ans = 0;
    while(bfs())
        ans += dfs(s, inf);
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> s >> t;
    while(m--)
    {
        int l, r, w;
        cin >> l >> r >> w;
        add(l, r, w);
        add(r, l, 0);
    }
    cout << dinic() << endl;
    return 0;
}

Dinic + 弧優化

在一次 dfs 求增廣路的圖中,每流完一條邊,可以保證不會再被使用,所以可以在本次 dfs 中刪除該邊

時間複雜度不變,但是速度提高非常大

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll inf = 1e17 + 10;
int head[maxn], nex[maxn], to[maxn], tp = 1, dep[maxn];
int cur[maxn];
ll vol[maxn];

int n, m, s, t;

void add(int l, int r, int w)
{
    tp++;
    nex[tp] = head[l];
    vol[tp] = w;
    to[tp] = r;
    head[l] = tp;
}

bool bfs()
{
    queue<int>q;
    for(int i=0; i<=n; i++) dep[i] = -1;
    for(int i=0; i<=n; i++) cur[i] = head[i];
    q.push(s);
    dep[s] = 0;
    while(q.size())
    {
        int now = q.front();
        q.pop();
        for(int i=head[now]; i; i=nex[i])
        {
            int u = to[i];
            if(dep[u] != -1 || vol[i] <= 0) continue;
            dep[u] = dep[now] + 1;
            q.push(u);
        }
    }
    return dep[t] != -1;
}

ll dfs(int now, ll flow)
{
    if(now == t)
        return flow;
    ll ans = 0;
    for(int i=cur[now]; i && flow; i=nex[i])
    {
        cur[now] = i;
        int u = to[i];
        if(vol[i] <= 0 || dep[u] != dep[now] + 1) continue;
        ll f = dfs(u, min(flow, vol[i]));
        vol[i] -= f;
        vol[i ^ 1] += f;
        ans += f;
        flow -= f;
    }
    return ans;
}

ll dinic()
{
    ll ans = 0;
    while(bfs())
        ans += dfs(s, inf);
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> s >> t;
    while(m--)
    {
        int l, r, w;
        cin >> l >> r >> w;
        add(l, r, w);
        add(r, l, 0);
    }
    cout << dinic() << endl;
    return 0;
}