有上下界网络流

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

引用 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
原文地址:https://www.cnblogs.com/zcr-blog/p/14335906.html