20220104-费用流

费用流

1. 最小费用最大流Minimum Cost Maximum Flow

  • 基本概念:网络上的每条边,除了容量外,还有一个属性:单位费用。一条边上的费用 = 流量 × 单位费用
  • 解决思路:我们已经知道,只要建了反向边,无论增广的顺序是怎样的,都能求出最大流。所以我们只需要每次都增广费用最少的一条路径即可。具体而言,将EK算法中的BFS换成SPFA即可;

2. 典型举例

  • 模板题

    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
    #include<bits/stdc++.h>
    using namespace std;

    const int N = 5010, M = 100010, INF = 1e8;

    int n, m, S, T;
    int h[N], e[M], f[M], w[M], ne[M], idx;
    int q[N], d[N], pre[N], incf[N];
    bool st[N];


    void add(int a, int b, int c, int d){
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
    e[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++];
    if(hh == N) hh = 0;
    st[t] = false;

    for(int i = h[t]; ~i; i = ne[i]){
    int ver = e[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] = true;
    }
    }
    }
    }
    return incf[T] > 0;
    }

    void EK(int& flow, int& cost){
    flow = cost = 0;
    while(spfa()){
    int t = incf[T];
    flow += t, cost += t * d[T];
    for(int i = T; i != S; i = e[pre[i] ^ 1]){
    f[pre[i]] -= t;
    f[pre[i] ^ 1] += t;
    }
    }
    }

    int main(){
    cin>>n>>m>>S>>T;
    memset(h, -1, sizeof h);
    while(m--){
    int a, b, c, d; cin>>a>>b>>c>>d;
    add(a, b, c, d);
    }

    int flow, cost;
    EK(flow, cost);
    cout<<flow<<" "<<cost<<endl;

    return 0;
    }
  • 运输问题

    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
    #include<bits/stdc++.h>
    using namespace std;

    const int N = 160, M = 5150 * 2 + 10, INF = 1e8;

    int n, m, S, T;
    int h[N], e[M], f[M], w[M], ne[M], idx;
    int q[N], d[N], pre[N], incf[N];
    bool st[N];

    void add(int a, int b, int c, int d){
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
    e[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++];
    if(hh == N) hh = 0;
    st[t] = false;
    for(int i = h[t]; ~i; i = ne[i]){
    int ver = e[i];
    if(f[i] && d[ver] > d[t] + w[i]){
    d[ver] = d[t] + w[i];
    pre[ver] = i;
    incf[ver] = min(incf[t], f[i]);
    if(!st[ver]){
    q[tt++] = ver;
    if(tt == N) tt = 0;
    st[ver] = true;
    }
    }
    }
    }
    return incf[T] > 0;
    }

    int EK(){
    int cost = 0;
    while(spfa()){
    int t = incf[T];
    cost += t * d[T];
    for(int i = T; i != S; i = e[pre[i] ^ 1]){
    f[pre[i]] -= t;
    f[pre[i] ^ 1] += t;
    }
    }
    return cost;
    }

    int main(){
    cin>>m>>n;
    S = 0, T = m + n + 1;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i++){
    int a; cin>>a;
    add(S, i, a, 0);
    }

    for(int i = 1; i <= n; i++){
    int b; cin>>b;
    add(m + i, T, b, 0);
    }

    for(int i = 1; i <= m; i++){
    for(int j = 1; j <= n; j++){
    int c; cin>>c;
    add(i, m + j, INF, c);
    }
    }

    cout<<EK()<<endl;

    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<<-EK()<<endl;

    return 0;
    }
  • 负载平衡问题

    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
    #include<bits/stdc++.h>
    using namespace std;

    const int N = 110, M = 610, INF = 1e8;
    int n, S, T;
    int s[N];
    int h[N], e[M], f[M], w[M], ne[M], idx;
    int q[N], d[N], pre[N], incf[N];
    bool st[N];

    void add(int a, int b, int c, int d){
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
    e[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++];
    if(hh == N) hh = 0;
    st[t] = false;

    for(int i = h[t]; ~i; i = ne[i]){
    int ver = e[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] = true;
    }
    }
    }
    }
    return incf[T] > 0;
    }

    int EK(){
    int cost = 0;
    while(spfa()){
    int t = incf[T];
    cost += t * d[T];
    for(int i = T; i !=S; i = e[pre[i] ^ 1]){
    f[pre[i]] -= t;
    f[pre[i] ^ 1] += t;
    }
    }
    return cost;
    }

    int main(){
    cin>>n;
    S = 0, T = n + 1;
    memset(h, -1, sizeof h);

    int tot = 0;
    for(int i = 1; i <= n; i++){
    cin>>s[i];
    tot += s[i];
    add(i, i < n ? i + 1 : 1, INF, 1);
    add(i, i > 1 ? i - 1 : n, INF, 1);
    }

    tot /= n;

    for(int i = 1; i <= n; i++){
    if(tot < s[i]) add(S, i, s[i] - tot, 0);
    else if(tot > s[i]) add(i, T, tot - s[i], 0);
    }

    cout<<EK()<<endl;

    return 0;
    }