• 有上下界网络流


    有上下界网络流的意思即为每条边不仅有最大流量,还有最小流量。除了需要满足每条边流量在此范围内以外,还需满足每个点流量平衡(除了源点和汇点)可以分为以下几个类型。

    引用 https://www.cnblogs.com/birchtree/p/12912607.html

    无源汇可行流

    即找到一种可行的流法。其实我们要做的其实就是转换为普通的最大流,我们强行给每条边跑最小流量,然后把最大流量减去最小流量作为新的流量上界(没有下界),但是这时可能不满足流量平衡,所以我们还要调整(利用残余网络的流量)。我们新建两个源点和汇点(ss和tt),如果一个点的流量是正的,那么从 (ss) 连一条到其的边,流量为这个点的流量,如果是负的就从其向 (tt) 流负的这个点的流量的边。然后跑最大流,如果 (ss,tt) 满流则说明存在可行流,为 (maxflow(ss,tt)+sum l_i)

    有源汇可行流

    设原网络的源和汇分别为 (s,t) 我们在原网络上加一条边 ((t,s,0,infty)),相当于把到汇点的所有流量都流回源点,这样每个点流量都守恒。 然后套无源汇的方法即可。注意总流量=t到s的无穷边在原图中的流量

    有源汇最大(最小)流

    先按上面的方法求出一个有源汇有上下界可行流.然后在附加网络上再跑一次 (s)(t) 的最大流(注意不是ss,tt!)。最大流=可行流+第二次跑的s到t最大流。

    再跑一次最大流是因为附加网络上属于原图的边还有流量没被“榨干”。容易发现只要附加网络上不属于原图的边满流,属于原图的边怎么跑流量都是守恒的。因为第一次跑最大流已经保证所有点守恒,第二次跑最大流不会经过不属于原图的边,因此等价于对原图跑一次普通的最大流,除源汇外流量守恒。两次合起来总流量一定守恒,这就保证了正确性。

    同理求最小流就跑一次 (t)(s) 的最大流。最小流=可行流-第二次跑的t到s最大流。这是因为Dinic过程中反向边的流量增加等价于正向边的的流量减少。

    有源汇最小费用流

    找到一条可行的最小费用流(不要求流最大)。

    按有源汇可行流的方法建图,把原图中的边带上费用(其他不带费用(0))。总费用=(mincost(ss,tt)+sum l_ic_i)

    例题

    luoguP5192

    首先不难看出这是一道网络流(从数据范围,题目描述中可以看出)。考虑怎么建图,由于又有上界又有下界所以考虑上下界有源汇最大流。

    考虑对于每一天建立一个点,每个女孩建立一个点,再建立一个源点和汇点,考虑从每个女孩连一条 ([G_i,infty]) 边向汇点,从源点向每天连一条 ([0,D_i]) 的边,然后每天向女孩连 ([l_i,r_i]) 然后跑上下界最大流即可。

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXM = 1005;
    const int MAXN = 370;
    const int inf = 0x7f7f7f7f;
    template <typename T>
    void read(T &x) {
        T flag = 1;
        char ch = getchar()kao;
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
        for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
        x *= flag;
    }
    struct node{
        int pre, to, val;
    }edge[1000005];
    int head[1000005], flow[1000005], d[1000005], cur[1000005], tot;
    int n, m;
    int G[MAXM], num;
    int gid[MAXM], did[MAXN], ans;
    queue<int> q;
    int S, T; 
    bool bfs(int s, int t) {
        for (int i = 1; i <= num; i++) cur[i] = head[i], d[i] = 0;
        d[s] = 1;
        q.push(s);
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            for (int i = head[x]; i; i = edge[i].pre) {
                int y = edge[i].to;
                if (edge[i].val > 0 && !d[y]) {
                    d[y] = d[x] + 1;
                    q.push(y);
                }
            }
        }
        return d[t];
    }
    int dinic(int x, int t, int now) {
        if (x == t || now == 0) return now;
        int rst = now;
        for (int i = cur[x]; i && rst; i = edge[i].pre) {
            int y = edge[i].to;
            if (edge[i].val > 0 && d[y] == d[x] + 1) {
                int k = dinic(y, t, min(rst, edge[i].val));
                if (!k) d[y] = 0;
                edge[i].val -= k;
                edge[i ^ 1].val += k;
                rst -= k;
            }
            cur[x] = i;
        }
        return now - rst;
    }
    void add_edge(int u, int v, int l) {
        edge[++tot] = node{head[u], v, l};
        head[u] = tot;
        edge[++tot] = node{head[v], u, 0};
        head[v] = tot;
    }
    void init() {
        for (int i = 1; i <= num; i++) flow[i] = head[i] = 0;
        tot = 1;
        num = 0;
        ans = 0;
        S = ++num, T = ++num;
    }
    int maxflow(int s, int t) {
        int ret = 0, now;
        while (bfs(s, t)) {
            while ((now = dinic(s, t, inf)) > 0) {
                ret += now;
            }
        }
        return ret;
    }
    int main() {
        while (scanf("%d%d", &n, &m) != EOF) {
            init();
            for (int i = 1; i <= m; i++) {
                read(G[i]);
                gid[i] = ++num;
                add_edge(gid[i], T, inf - G[i]);
                flow[gid[i]] -= G[i];
                flow[T] += G[i];
            }
            for (int i = 1; i <= n; i++) {
                int c, D;
                read(c); read(D);
                did[i] = ++num;
                add_edge(S, did[i], D);
                for (int j = 1; j <= c; j++) {
                    int t, l, r;
                    read(t); read(l); read(r);
                    t++;
                    add_edge(did[i], gid[t], r - l);
                    flow[did[i]] -= l;
                    flow[gid[t]] += l;
                }
            }
            add_edge(T, S, inf);
            int e = tot ^ 1;
            int ss = ++num, tt = ++num;
            for (int i = 1; i <= num - 2; i++) {
                if (flow[i] > 0) {
                    add_edge(ss, i, flow[i]);
                } else if (flow[i] < 0) {
                    add_edge(i, tt, -flow[i]);
                }
            }
            bool flag = true;
            maxflow(ss, tt);
            ans = inf - edge[e].val;
            edge[e].val = edge[e ^ 1].val = 0;
            for (int i = head[ss]; i; i = edge[i].pre) {
                if (edge[i].val > 0) {
                    flag = false;
                }
            }
            if (flag) {
                printf("%d
    
    ", ans + maxflow(S, T));
            } else {
                puts("-1");
                printf("
    ");
            }
        }
        return 0;
    }
    

    CF704D

    Hint 1 考虑每个点有两个性质(x,y),那么可以对这两个性质分别建立两个点,对于每条限制的直线建立一个点。
    Hint 2 我们不妨设 $rle b$,那么我们建图流的意义即为选红色的点的个数。
    Solution
  • 相关阅读:
    android学习(2) 多线程的理解
    activity学习(1) 生命周期理解
    webkit.net使用方法日记
    微信小程序入门一
    ES入门笔一
    node八-核心模块、包
    require、缓存
    node七-required、缓存
    Node六-模块化
    Node笔记五-进程、线程
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/14335906.html
Copyright © 2020-2023  润新知