Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Graphs / Flows and Cuts

4.5.7 Minimum-Cost Maximum-Flow (Potentials)

4-Graphs/4.5.7_Minimum-Cost_Maximum-Flow_(Potentials).cpp

View on GitHub

Given a flow network with capacities and edge costs, find a minimum-cost flow from a source node to a sink node. This is the successive shortest path method with Johnson potentials: after initial potentials make all residual reduced costs nonnegative, each augmenting path is found with Dijkstra's algorithm.

  • MinCostMaxFlow<T, C>(n) constructs an empty residual network with nodes numbered [0, n).
  • add_edge(u, v, cap, cost, rev_cap = 0) adds a directed residual-network edge and returns its edge ID.
  • edge_flow(id) returns the flow through a previously added edge.
  • clear_flow() resets all edge flows to zero.
  • min_cost_flow(source, sink, target_flow) sends up to target_flow additional units of flow and returns (flow, cost) for the flow sent by that call. If the returned flow is smaller than target_flow, the residual network cannot carry the requested amount.

If all initial edge costs are nonnegative, the initial potentials can be zero and the first shortest path search is already Dijkstra-safe. If negative costs are present, this implementation first runs SPFA to compute valid initial potentials; it assumes there is no reachable negative-cost cycle. The capacity type T should be signed, since reverse residual edges store negative flow.

Implementation

#include <algorithm>
#include <cstdint>
#include <functional>
#include <limits>
#include <queue>
#include <utility>
#include <vector>

template<class T, class C = int64_t>
class MinCostMaxFlow {
  struct Edge {
    int u, v;
    T cap, flow;
    C cost;
  };

  static constexpr T EPS = static_cast<T>(1e-9);
  static bool residual(const Edge &e) { return e.cap - e.flow > EPS; }

  int nodes;
  std::vector<Edge> edges;
  std::vector<std::vector<int>> adj;
  std::vector<C> potential;

  void init_potentials(int source) {
    static const C INF_COST = std::numeric_limits<C>::max() / 4;
    std::fill(potential.begin(), potential.end(), 0);
    bool has_negative_cost = false;
    for (const Edge &e : edges) {
      has_negative_cost |= residual(e) && e.cost < 0;
    }
    if (!has_negative_cost) {
      return;
    }
    std::fill(potential.begin(), potential.end(), INF_COST);
    std::vector<bool> in_queue(nodes);
    std::queue<int> q;
    potential[source] = 0;
    q.push(source);
    in_queue[source] = true;
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      in_queue[u] = false;
      for (int id : adj[u]) {
        const Edge &e = edges[id];
        if (residual(e) && potential[u] + e.cost < potential[e.v]) {
          potential[e.v] = potential[u] + e.cost;
          if (!in_queue[e.v]) {
            q.push(e.v);
            in_queue[e.v] = true;
          }
        }
      }
    }
    for (C &p : potential) {
      if (p == INF_COST) {
        p = 0;
      }
    }
  }

 public:
  explicit MinCostMaxFlow(int n) : nodes(n), adj(n), potential(n, 0) {}

  int add_edge(int u, int v, T cap, C cost, T rev_cap = 0) {
    int id = static_cast<int>(edges.size());
    adj[u].push_back(id);
    edges.push_back(Edge{u, v, cap, 0, cost});
    adj[v].push_back(id + 1);
    edges.push_back(Edge{v, u, rev_cap, 0, -cost});
    return id;
  }

  T edge_flow(int id) const { return edges[id].flow; }

  void clear_flow() {
    for (Edge &e : edges) {
      e.flow = 0;
    }
    std::fill(potential.begin(), potential.end(), 0);
  }

  std::pair<T, C> min_cost_flow(int source, int sink, T target_flow) {
    static const C INF_COST = std::numeric_limits<C>::max() / 4;
    init_potentials(source);
    T flow = 0;
    C cost = 0;
    std::vector<C> dist(nodes);
    std::vector<int> parent_edge(nodes);
    while (flow < target_flow) {
      std::fill(dist.begin(), dist.end(), INF_COST);
      std::priority_queue<
          std::pair<C, int>, std::vector<std::pair<C, int>>, std::greater<std::pair<C, int>>>
          pq;
      dist[source] = 0;
      pq.push({0, source});
      while (!pq.empty()) {
        auto [du, u] = pq.top();
        pq.pop();
        if (du != dist[u]) {
          continue;
        }
        for (int id : adj[u]) {
          const Edge &e = edges[id];
          C reduced_cost = e.cost + potential[u] - potential[e.v];
          if (residual(e) && dist[u] + reduced_cost < dist[e.v]) {
            dist[e.v] = dist[u] + reduced_cost;
            parent_edge[e.v] = id;
            pq.push({dist[e.v], e.v});
          }
        }
      }
      if (dist[sink] == INF_COST) {
        break;
      }
      for (int i = 0; i < nodes; i++) {
        if (dist[i] < INF_COST) {
          potential[i] += dist[i];
        }
      }
      T aug = target_flow - flow;
      for (int v = sink; v != source; v = edges[parent_edge[v]].u) {
        const Edge &e = edges[parent_edge[v]];
        aug = std::min(aug, e.cap - e.flow);
      }
      for (int v = sink; v != source; v = edges[parent_edge[v]].u) {
        int id = parent_edge[v];
        Edge &e = edges[id];
        e.flow += aug;
        edges[id ^ 1].flow -= aug;
        cost += aug * e.cost;
      }
      flow += aug;
    }
    return {flow, cost};
  }
};

Example Usage

#include <cassert>
using namespace std;

int main() {
  MinCostMaxFlow<int> g(6);
  // Example graph after max flow, with each edge labeled flow/capacity:
  //            2/2
  //       1 --------> 3
  //      / \          |
  // 3/4 /   \ 1/1     | 2/2
  //    /     v        v
  //   0       4 ----> 5
  //    \     ^   3/3
  // 2/3 \   / 2/2
  //      v /
  //       2
  int id01 = g.add_edge(0, 1, 4, 1);
  g.add_edge(0, 2, 3, 1);
  g.add_edge(1, 3, 2, 1);
  g.add_edge(1, 4, 1, 2);
  g.add_edge(2, 4, 2, 1);
  g.add_edge(3, 5, 2, 1);
  g.add_edge(4, 5, 3, 1);
  auto [flow, cost] = g.min_cost_flow(0, 5, 5);
  assert(flow == 5);
  assert(cost == 16);
  assert(g.edge_flow(id01) == 3);
  g.clear_flow();
  auto [flow2, cost2] = g.min_cost_flow(0, 5, 5);
  assert(flow2 == 5);
  assert(cost2 == 16);
  return 0;
}