有源汇上下界最大流

  1. 1. 前言
  • 有源汇上下界最大流
    1. 基础
    2. 建模
    3. 什么是有源汇上下界最小流
      1. 1. 求解无源汇可行流
      2. 2. 有源汇上下界可行流
      3. 3. 有源汇上下界最大流
      4. 4. 有源汇上下界最小流

  • 前言

    终于来啦!

    • 拖了很长时间的博客(反正也没人看,我爱咋拖咋拖)。刚刚集训回来不知道干啥,更新一下博客,抽时间写个游记。

    有源汇上下界最大流

    基础

    1. 网络流(不会的可以搜索,或者看看本蒟蒻的 blog,这里略过)
    2. 上下界网络流相关问题(这个不会没有关系,我下面讲)

    建模

    一到比较裸的有源汇上下界最小流。每条边必走一次,要求求出最小的流量。由于比较裸,这里当作上下界流的例题讲。

    什么是有源汇上下界最小流

    顾名思义,就是在最大流的基础上增加了边的最小经过流量,使得整个网络可行,并且找出最小流量的方案。为了简化问题,我们先从无源汇上下界可行流说起。

    求解无源汇可行流

    一个比较朴实的想法就是将一个边的最大限制减去最小限制,跑最大流即可。但是该方法并不满足流量守恒定律,因此做出一下调整。

    我们在图中新建两个虚拟点,设置为新的虚拟源汇点 。设 为点 所有入度边中的流量下限与所有出度中的流量下限之差。接下来我们对 的大小关系进行分类讨论。

    • 则将该点与新源点 连接,流量为
    • 则将该点与新汇点 连接,流量为
    • ,我们不需要考虑,反正不管怎么连流量都是

    为什么要这么连呢,我们可以分析一下网络流的过程,由于我们直接连接的是流量上限 与 流量下限 的差作为该边的流量的,这会导致本来可以在 以内退流退掉的流量被清空了(可以回忆一下找增广路的退流过程)。我们对某一点 的所有 情况进行汇总,记录一个 来表示所有入度边中的流量下限与所有出度中的流量下限之差,可以理解为判断该点在总体上是流入还是流出,以此来弥补未能正确退流的流量。

    注意:在原图中的原来的源汇点也需要与新源汇点进行连接。

    有源汇上下界可行流

    有无源汇的最大区别就是源汇点是否满足流量守恒,我们在处理无源汇问题的时候不需要考虑源汇点的流量是否守恒,默认源点能无限产生,汇点能无限吸收。但在有源汇问题中考虑了源汇点的流量守恒性,不过处理起来也非常简单,我们只要让源汇点并入网络,也就是让汇点源点连一条流量为无穷的边即可(注意这里的源汇点皆为旧源汇点)。

    有源汇上下界最大流

    这个其实也很好处理,在跑完一边有源汇上下界可行流之后记录当前的最大流量(即汇点源点的流量),判断一下当前网络流是否为满流,若不是,则无解,否这断开汇点源点的边,跑一次无源汇上下界可行流,答案即为当前流加上之前记录的最大流。

    有源汇上下界最小流

    和最大流类似,只不过是先处理无源汇上下界可行流,此时就已经将该网络中的所以增广路全部跑满,然后再连接汇点向源点的边,此时的最大流便是满足上下界要求的最小流。

    源点和汇点已经加粗,一定要注意新旧源汇点的区别!

    Code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    #include <bits/stdc++.h>
    using namespace std;
    int n, m;
    const int N = 1e6 + 10;
    const int M = 1e6 + 10;
    const int inf = 0x3f3f3f3f;
    int head[N];
    int now[N];
    int cnt = 1;
    struct edge {
    int u, v, c, val, nxt;
    edge(int u = 0, int v = 0, int c = 0, int val = 0, int nxt = 0) : u(u), v(v), c(c), val(val), nxt(nxt) {}
    } e[M];
    void ADD(int u, int v, int c, int val) {
    cnt++;
    e[cnt] = edge(u, v, c, val, head[u]);
    head[u] = cnt;
    }
    void add_edge(int u, int v, int c, int val) {
    ADD(u, v, c, val);
    ADD(v, u, 0, -val);
    }
    int d[N];
    void add_limit(int u, int v, int l, int r) {
    add_edge(u, v, r - l, 0);
    d[u] -= l;
    d[v] += l;
    }
    int s, t;
    int dep[N];
    bool bfs() {
    memset(dep, 0, sizeof dep);
    dep[s] = 1;
    queue<int>q;
    q.push(s);
    while (!q.empty()) {
    int u = q.front();
    q.pop();
    now[u] = head[u];
    for (int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].v;
    int c = e[i].c;
    if (dep[v] || !c) continue;
    dep[v] = dep[u] + 1;
    q.push(v);
    }
    }
    return dep[t] != 0;
    }
    int dfs(int u, int t, int flow) {
    if (u == t) return flow;
    int nowflow = 0;
    for (int i = now[u]; i && nowflow < flow; i = e[i].nxt) {
    now[u] = i;
    int v = e[i].v;
    int c = e[i].c;
    if (dep[v] != dep[u] + 1 || !c) continue;
    int ff = dfs(v, t, min(flow - nowflow,c));
    if (ff)e[i].c -= ff, e[i ^ 1].c += ff, nowflow += ff;
    }
    return nowflow;
    }
    int maxflow() {
    int ans = 0;
    while (bfs()) {
    int nowflow;
    while ((nowflow = dfs(s, t, inf))) ans += nowflow;
    }
    return ans;
    }
    void work(int n,int m) {
    memset(head,0,sizeof head);
    memset(d,0,sizeof d);
    cnt=1;
    int tot = 0;
    int ss, tt;
    ss = n + m + 1, tt = n + m + 2;
    s = n + m + 3, t = n + m + 4;
    for (int i = 1; i <= m; i++) {
    int g;
    cin >> g;
    add_limit(n + i, tt, g, inf);
    }
    for (int i = 1; i <= n; i++) {
    int k, c;
    cin >> k >> c;
    add_edge(ss, i, c, 0);
    for (int j = 1; j <= k; j++) {
    int t, l, r;
    cin >> t >> l >> r;
    t++;
    add_limit(i, n + t, l, r);
    }
    }

    for (int i = 1; i <= m + n + 2; i++) {
    if (d[i] > 0) add_edge(s, i, d[i], 0), tot += d[i];
    else add_edge(i, t, -d[i], 0);
    }
    add_edge(tt, ss, inf, 0);
    if (maxflow() != tot) {
    puts("-1\n");
    return;
    }
    int ans = e[cnt].c;
    e[cnt].c = e[cnt ^ 1].c = 0;
    s = ss, t = tt;
    ans += maxflow();
    cout << ans << endl<<endl;
    }
    int main()
    {
    while(~scanf("%d%d",&n,&m))
    work(n,m);
    }