zoukankan      html  css  js  c++  java
  • 网络流模板

    网络流模板

    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
    ", 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
    ", 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_{uin V}f(u, x)),进行剪掉 (l(u, x)),后,设 (F_{in, sub} = sum_{uin V}l(u, x))
    假设点左边都是满流,那么初始从点 (x) 流出去的流量设为 (F_{out}),那么 (F_{in} = sum_{vin V}f(x, v)),进行剪掉 (l(x, v)),后,设 (F_{in, sub} = sum_{vin 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
    ", 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
    %d
    ", min_cost, max_cost);
        }
    }EK;
    
    int a[N];
    void solve() {
        EK.make_map();
        
    }
    signed main() {
       ll t = 1;//cin >> t;
       while (t--) {
          solve();
       }
    }
    
  • 相关阅读:
    [导入]【翻译】WF从入门到精通(第十三章):打造自定义活动
    [导入]关于网页标准与JAVAScript执行的问题
    html包含html文件的方法
    [导入]C#加密方法汇总
    8、步步为营VS 2008 + .NET 3.5(8) DLINQ(LINQ to SQL)之面向对象的添加、查询、更新和删除
    [导入]【翻译】WF从入门到精通(第十五章):工作流和事务
    [导入]存储过程得到某个表的所有字段信息
    1、步步为营VS 2008 + .NET 3.5(1) VS 2008新特性之Multi Targeting(多定向)、Web Designer and CSS(集成了CSS的web设计器)和Nested Master Page(嵌套母版页)
    [导入]vbs修改注册表
    正则表达式30分钟入门教程
  • 原文地址:https://www.cnblogs.com/Xiao-yan/p/14649865.html
Copyright © 2011-2022 走看看