网络流模板

网络流模板

EK求最大可行流

#include <iostream>
#include <queue>
#include <cstring>


using namespace std;
const int N = 2000, M = 20090;
int h[N], ne[M], to[M], f[M], idx;
int s, t;
int pre[N], d[N];
queue<int>q;
void add(int u, int v, int w) {
    ne[idx] = h[u], to[idx] = v, f[idx] = w, h[u] = idx++;
    ne[idx] = h[v], to[idx] = u, f[idx] = 0, h[v] = idx++;
}
bool vis[N];
bool bfs() {
    while (!q.empty())q.pop();
    q.push(s);
    memset(vis, 0, sizeof vis);
    vis[s] = 1;
    d[s] = 99999999;//d数组是指可行流。
    while (!q.empty()) {
        auto u = q.front();
        q.pop();
        for (int i = h[u]; ~i; i = ne[i]) {
            int v = to[i];
            if (!vis[v] && f[i] > 0) {
                vis[v] = 1;
                q.push(v);
                pre[v] = i;//记录路径,这样找到了增广路,简单可回溯。
                d[v] = min(f[i], d[u]);//因为是可行流,所以每次要取最小。
                if (v == t)return 1;
            }
        }
    }
    return 0;
}
int EK() {
    int ret = 0;
    while (bfs()) {//如果能够找到增广路,那么把那条增广路上的可行流加入答案,然后
        int T = t;
        ret += d[t];
        while (T != s) {//更新这条增广路上的所有流量,即更新了残留网络。
            int now = pre[T];//now是指增广路中指向T点的边的编号
            f[now] -= d[t];
            f[now^1]+=d[t];
            T = to[now^1]; //now^1是指在增广路上从T指出的编号。
        }
    }
    return ret;
}
int main() {
    int n, m;
    scanf("%d%d%d%d", &n, &m, &s, &t);
    memset(h, -1, sizeof h);idx = 0;
    while (m--) {
        int u, v, w;scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);//正常建立边,然后构造残留网络,显然。
    }
    printf("%d\n", EK());
}

dinic求最大可行流

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

const int N = 10009, M = 200010;
const int inf = 99999999;
int n, m, S, T;
int h[N], ne[M], to[M], f[M], idx;
void add(int u, int v, int w) {
    ne[idx] = h[u], to[idx] = v, f[idx] = w, h[u] = idx ++;
    ne[idx] = h[v], to[idx] = u, f[idx] = 0, h[v] = idx ++;
}
queue<int>q;
int d[N];
int cur[N];
bool bfs() {
    while (!q.empty()) q.pop();
    q.push(S);
    memset(d, -1, sizeof d);//d数组是深度,防止绕进环中去。
    d[S] = 0;
    cur[S] = h[S];//必然第一个弧就是,但是容易忘。
    while (!q.empty()) {
        auto u = q.front();q.pop();
        for (int i = h[u]; ~i; i = ne[i]) {
            int v = to[i];
            if (f[i] && d[v] == -1) {//找增广路,所以必须f[i] > 0
                d[v] = 1 + d[u];
                cur[v] = h[v];//只会遍历一次。
                if (v == T)return 1;
                q.push(v);
            }
        }
    }
    return 0;
}
int dfs(int u, int limit) {
    if (u == T)return limit;//容易忘。终点直接返回
    int flow = 0;//点流向后面的。
    for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {//从当前弧开始,如果之前
        int v = to[i];
        cur[u] = i;//回溯到u点之前,然后又到了u点,没必要从头开始了。
        if (d[v] == d[u] + 1 && f[i]) {//d是按照层数走,f必须大于0。
            int t = dfs(v, min(f[i], limit - flow));//t是走向出去的流量。
            if (t <= 0) d[v] = -1;//一滴也没有了,点就可以删掉了。
            f[i] -= t;
            f[i^1] += t;//更新残留网络。
            flow += t;//当前总的流向后面的加上当前v流向后面的。
        }
    }
    return flow;
}
int dinic() {
    int ret = 0, flow;
    while (bfs()) {
        while (flow = dfs(S, inf))ret +=flow;
    }
    return ret;
}
int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d%d%d", &n, &m, &S, &T);
    for (int i = 1; i <= m; i ++) {
        int u, v, w;
        scanf("%d%d%d",&u,&v,&w);
        add(u, v, w);
    }
    printf("%d\n", dinic());
}
struct DI {
    ll n, m, S, T;
    ll h[N], ne[M], to[M], f[M], idx;
    void add(ll u, ll v, ll w) {
        ne[idx] = h[u], to[idx] = v, f[idx] = w, h[u] = idx++;
        ne[idx] = h[v], to[idx] = u, f[idx] = 0, h[v] = idx++;
    }
    queue<ll> q;
    ll d[N];
    ll cur[N];
    void init() {
        memset(h, -1, sizeof h);
		idx = 0;
    }
    bool bfs() {
        while (!q.empty()) q.pop();
        q.push(S);
        memset(d, -1, sizeof d);  // d数组是深度,防止绕进环中去。
        d[S] = 0;
        cur[S] = h[S];  //必然第一个弧就是,但是容易忘。
        while (!q.empty()) {
            auto u = q.front();
            q.pop();
            for (ll i = h[u]; ~i; i = ne[i]) {
                ll v = to[i];
                if (f[i] && d[v] == -1) {  //找增广路,所以必须f[i] > 0
                    d[v] = 1 + d[u];
                    cur[v] = h[v];  //只会遍历一次。
                    if (v == T) return 1;
                    q.push(v);
                }
            }
        }
        return 0;
    }
    ll dfs(ll u, ll limit) {
        if (u == T) return limit;  //容易忘。终点直接返回
        ll flow = 0;               //点流向后面的。
        for (ll i = cur[u]; ~i && flow < limit;
             i = ne[i]) {  //从当前弧开始,如果之前
            ll v = to[i];
            cur[u] = i;  //回溯到u点之前,然后又到了u点,没必要从头开始了。
            if (d[v] == d[u] + 1 && f[i]) {  // d是按照层数走,f必须大于0。
                ll t = dfs(v, min(f[i], limit - flow));  // t是走向出去的流量。
                if (t <= 0) d[v] = -1;  //一滴也没有了,点就可以删掉了。
                f[i] -= t;
                f[i ^ 1] += t;  //更新残留网络。
                flow += t;  //当前总的流向后面的加上当前v流向后面的。
            }
        }
        return flow;
    }
    ll dinic() {
        ll ret = 0, flow;
        while (bfs()) {
            while (flow = dfs(S, inf)) ret += flow;
        }
        // init();
        return ret;
    }
    void makemap() {
        init();
        scanf("%d", &n);
        S = N-2;
        T = N - 1;
        for (int i = 1; i <= n * 2; i++) add(S, i, 1);
        for (int i = 1; i <= 2 * n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            u += 3 * n;
            v += 3 * n;
            add(i, u, 1);
            add(i, v, 1);
        }
        for (int i = 1; i <= n; i++) {
            add(i + 3 * n, T, 2);
        }
    }
} dinic;

无源汇上下界可行流

首先可转化为有源汇上界最大流,即普通最大流。首先,某个点要满足两个限制,一个是流量守恒,另一个是容量守恒。
\(l(u, v)\) 为点 \(u\) 到点 \(v\) 的下界,然后设 \(u(u, v)\) 是点 \(u\) 到点 \(v\) 的上界。
显然 $$l(u, v) <= f(u, v) <= u(u, v) $$
如果突发奇想,全减掉 \(l(u, v)\),那么左边就是 \(0\),是不是就转化成了普通的流量网络?即

\[0 <= f(u, v) - l(u, v) <= u(u, v) - l(u, v) \]

但是流量守恒却不一定。
假设点左边都是满流,那么初始进入点 \(x\) 的流量设为 \(F_{in}\),那么 \(F_{in} = \sum_{u\in V}f(u, x)\),进行剪掉 \(l(u, x)\),后,设 \(F_{in, sub} = \sum_{u\in V}l(u, x)\)
假设点左边都是满流,那么初始从点 \(x\) 流出去的流量设为 \(F_{out}\),那么 \(F_{in} = \sum_{v\in V}f(x, v)\),进行剪掉 \(l(x, v)\),后,设 \(F_{in, sub} = \sum_{v\in V}l(x, v)\)
所以当 \(F_{in, sub}\) 多了,即左边减得太多了,就应该是从 \(S\) 加一条边到 \(x\),然后容量限制为 \(F_{in, sub} - F_{out, sub}\),如果右边剪得太多了,那么就应该从 \(x\) 加一条边到 \(T\),然后容量限制为 \(F_{out,sub} - F_{in, sub}\)
然后代码中 \(A\) 数组代表的意义是 \(F_{in, sub} - F_{out, sub}\)

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5, M = 2e6;
const int inf = 0x3f3f3f3f;
int n, m, S, T;
int h[N], ne[M], l[M], to[M], f[M], idx; 
int A[N];
void add(int u, int v, int w, int low) {
    ne[idx] = h[u], to[idx] = v, f[idx] = w, l[idx] = low, h[u] = idx++;
    ne[idx] = h[v], to[idx] = u, f[idx] = 0, l[idx] = low, h[v] = idx++;
}
queue<int>q;
int d[N], cur[N];
bool bfs() {
    while (!q.empty())q.pop();
    q.push(S);
    memset(d, -1, sizeof d);
    d[S] = 0;
    cur[S] = h[S];
    while (!q.empty()) {
        auto u = q.front();q.pop();
        for (int i = h[u]; ~i; i = ne[i]) {
            int v = to[i];
            if (f[i] && d[v] == -1) {
                d[v]  = d[u] + 1;
                cur[v] = h[v];
                q.push(v);
                if (v == T)return 1;
            }
        }
    }
    return 0;
}
int dfs(int u, int limit) {
    if (u == T)return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
        int v = to[i];
        cur[u] = i;
        if (d[v] == d[u] + 1 && f[i]) {
            int t = dfs(v, min(f[i], limit - flow));
            if (t <=0)d[v] = -1;
            flow += t, f[i] -= t, f[i ^ 1] += t;
        }
    }
    return flow;
}
int dinic() {
    int ret = 0, flow;
    while (bfs())while (flow = dfs(S, inf))ret += flow;
    return ret;
}
int main() {
    int sum = 0;
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    for (int i = 1; i <= m; i ++) {
        int u, v, low, up;
        scanf("%d%d%d%d", &u, &v, &low, &up);
        int w = up - low;
        A[u] -= low;
        A[v] += low;
        add(u, v, w, low);
    }
    S = 0, T = n + 1;
    for (int i = 1; i <= n; i ++) {
        if (A[i] > 0) {
            add(S, i, A[i], 0);
            sum += A[i];
        } else if (A[i] < 0) {
            add(i, T, -A[i], 0);
        }
    }
    int max_flow = dinic();
    if (max_flow == sum) {
        puts("YES");
        for (int i = 0; i < m * 2; i += 2) {
            printf("%d\n", f[i ^ 1] + l[i]);
        }
        return 0;
    }
    puts("NO");
}

最小费用最大流

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
const ll N = 1e6 + 9;
const ll inf = 0x3f3f3f3f;
const int M = 2 * N;


struct costflow {
    int n, m, S, T;
    int h[N], to[M], f[M], w[M], ne[M], idx;
    int q[N], d[N], pre[N], incf[N];
    bool st[N];
    void add(int u, int v, int flow, int co) {
        int a = u, b = v, c = flow, d = co;
        to[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
        to[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx++;
    }

    bool spfa() {
        int hh = 0, tt = 1;
        memset(d, 0x3f, sizeof d);
        memset(incf, 0, sizeof incf);
        q[0] = S, d[S] = 0, incf[S] = inf;
        while (hh !=tt) {
            int t = q[hh++];
            //cout << "?";
            if (hh == N)hh  =0;
            st[t] = false;
            for (int i = h[t]; ~i; i = ne[i]) {
                int ver = to[i];
                if (f[i] && d[ver] > d[t] + w[i]) {
                    d[ver] = d[t] + w[i];
                    pre[ver] = i;
                    incf[ver] = min(f[i], incf[t]);
                    if (!st[ver]) {
                        q[tt++] = ver;
                        if (tt == N) tt = 0;
                        st[ver] = 1;
                    }
                }
            }
        }
        return incf[T] > 0;
    }
    void EK(int &flow, int &Max_cost, int &Min_cost) {
        flow = Min_cost = 0;
        while (spfa()) {
            int t = incf[T];
            flow += t, Min_cost += t * d[T];
            for (int i = T; i != S; i = to[pre[i]^1]) {
                f[pre[i] ] -= t;
                f[pre[i] ^ 1] += t;
            }
        }
        for (int i = 0; i <= idx; i += 2) {
            f[i] += f[i ^ 1];
            f[i^1] = 0;
            w[i] = -w[i];
            w[i ^ 1] = -w[i ^ 1];
        }
        //cout << Min_cost << "?";
        flow = Max_cost = 0;
        while (spfa()) {
            int t = incf[T];
            flow += t, Max_cost += t * d[T];
            for (int i = T; i != S; i = to[pre[i] ^ 1]) {
                f[pre[i]] -= t;
                f[pre[i] ^ 1] += t;
            }
        }
        Max_cost*=-1;
    }
    void init() {
      memset(h, -1, sizeof h);
    idx = 0;
    }
    int a[N];
    void make_map() {
        init();
        scanf("%d%d", &n, &m);
        S = N-2, T = N - 1;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            add(S, i, a[i], 0);
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d", &a[i]);
            add(i + n, T, a[i], 0);
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                int x;
                scanf("%d", &x);
                add(i, j + n, inf, x);
            }
        }
        int flow, min_cost, max_cost;
        // cout << "?";
        EK(flow, max_cost, min_cost);
        printf("%d\n%d\n", min_cost, max_cost);
    }
}EK;

int a[N];
void solve() {
    EK.make_map();
    
}
signed main() {
   ll t = 1;//cin >> t;
   while (t--) {
      solve();
   }
}
posted @ 2021-04-12 20:25  u_yan  阅读(47)  评论(0编辑  收藏  举报