0

I've been trying to understand this code; it's an implementation of the push-relabel algorithm in C++:

// Adjacency list implementation of FIFO push relabel maximum flow
// with the gap relabeling heuristic.  This implementation is
// significantly faster than straight Ford-Fulkerson.  It solves
// random problems with 10000 vertices and 1000000 edges in a few
// seconds, though it is possible to construct test cases that
// achieve the worst-case.
//
// Running time:
//     O(|V|^3)
//
// INPUT: 
//     - graph, constructed using AddEdge()
//     - source
//     - sink
//
// OUTPUT:
//     - maximum flow value
//     - To obtain the actual flow values, look at all edges with
//       capacity > 0 (zero capacity edges are residual edges).

#include <cmath>
#include <vector>
#include <iostream>
#include <queue>

using namespace std;

typedef long long LL;

struct Edge {
  int from, to, cap, flow, index;
  Edge(int from, int to, int cap, int flow, int index) :
    from(from), to(to), cap(cap), flow(flow), index(index) {}
};

struct PushRelabel {
  int N;
  vector<vector<Edge> > G;
  vector<LL> excess;
  vector<int> dist, active, count;
  queue<int> Q;

  PushRelabel(int N) : N(N), G(N), excess(N), dist(N), active(N), count(2*N) {}

  void AddEdge(int from, int to, int cap) {
    G[from].push_back(Edge(from, to, cap, 0, G[to].size()));
    if (from == to) G[from].back().index++;
    G[to].push_back(Edge(to, from, 0, 0, G[from].size() - 1));
  }

  void Enqueue(int v) { 
    if (!active[v] && excess[v] > 0) { active[v] = true; Q.push(v); } 
  }

  void Push(Edge &e) {
    int amt = int(min(excess[e.from], LL(e.cap - e.flow)));
    if (dist[e.from] <= dist[e.to] || amt == 0) return;
    e.flow += amt;
    G[e.to][e.index].flow -= amt;
    excess[e.to] += amt;    
    excess[e.from] -= amt;
    Enqueue(e.to);
  }

  void Gap(int k) {
    for (int v = 0; v < N; v++) {
      if (dist[v] < k) continue;
      count[dist[v]]--;
      dist[v] = max(dist[v], N+1);
      count[dist[v]]++;
      Enqueue(v);
    }
  }

  void Relabel(int v) {
    count[dist[v]]--;
    dist[v] = 2*N;
    for (int i = 0; i < G[v].size(); i++) 
      if (G[v][i].cap - G[v][i].flow > 0)
    dist[v] = min(dist[v], dist[G[v][i].to] + 1);
    count[dist[v]]++;
    Enqueue(v);
  }

  void Discharge(int v) {
    for (int i = 0; excess[v] > 0 && i < G[v].size(); i++) Push(G[v][i]);
    if (excess[v] > 0) {
      if (count[dist[v]] == 1) 
    Gap(dist[v]); 
      else
    Relabel(v);
    }
  }

  LL GetMaxFlow(int s, int t) {
    count[0] = N-1;
    count[N] = 1;
    dist[s] = N;
    active[s] = active[t] = true;
    for (int i = 0; i < G[s].size(); i++) {
      excess[s] += G[s][i].cap;
      Push(G[s][i]);
    }

    while (!Q.empty()) {
      int v = Q.front();
      Q.pop();
      active[v] = false;
      Discharge(v);
    }

    LL totflow = 0;
    for (int i = 0; i < G[s].size(); i++) totflow += G[s][i].flow;
    return totflow;
  }
};

The code compiles and works, but I can't understand how I should pass input to it. Ideally a main() function should read the source and the sink (which are "imaginary" anyway and are only neccessary for the algorithm to work), and then all the data neccessary to build the graph with AddEdge(). But how to do that is currently beyond me.

My main() should look like this

int main()
{
    int source, sink;
    vector < vector < int > > graph;
    std::cin >> source >> sink;
    //here should be the nested for loops to read in the graph

    // and here the arguments should be passed to AddEdge
    return 0;
}

I think that source should be used to iniialise some Edge.froms, and it should be similar with sink and some Edge.tos, but I can't figure how to build a graph.

martin
  • 3,149
  • 1
  • 24
  • 35
Chiffa
  • 1,486
  • 2
  • 19
  • 38
  • 1
    Questions asking how to use some third party library would be considered off-topic. – Cory Kramer Aug 13 '14 at 14:14
  • Interesting. I provide a link to code written by someone else, which uses STL libraries. What's particularly off-topic about that? – Chiffa Aug 13 '14 at 14:22
  • Please show your current attempts and explain how they didn't work. Put more information, such as a specific function and its parameters, in your question. – Neil Kirk Aug 13 '14 at 14:31

1 Answers1

1

Create an instance of the struct PushRelabel(N) with N nodes:

struct PushRelabel prGraph(N);

Then you build the graph with the AddEdge function. Assuming the nice Edge structure, this could be done as

std::vector<Edge> edges;

for( auto e : edges )
  prGraph.AddEdge(e.from, e.to, e.cap);

Then there is a GetMaxFlow function taking your source s and sink t which computes and returns the flow:

int s, t;
auto flow = prGraph.getMaxFlow(s, t);

Of course, you need to read all the edges and capacities and the number of nodes yourself, I hope this is a starting point.


int main()
{
   struct PushRelabel prGraph(4);
   prGraph.AddEdge(0,1,2);
   prGraph.AddEdge(0,2,2);
   prGraph.AddEdge(1,3,1);
   prGraph.AddEdge(2,3,3);

   std::cout<< prGraph.GetMaxFlow(0,3) << std::endl;
}

For this graph with source s = 0 and sink t = 3

    1
   / \
c(2)  c(1)
 /     \
0       3
 \     /
c(2)  c(3)  
   \ /
    2

Outputs 3, as it should.

martin
  • 3,149
  • 1
  • 24
  • 35
  • Ingenious. It's defintely a starting point, which I'll try to go on from. As for the matrix, I'll be given enough data to figure out the edges and capacities. – Chiffa Aug 13 '14 at 16:09
  • @Martin Do you by any chance help me with coming up a suitable way to get the min-cut? – Morpheus Dec 17 '18 at 15:45
  • @Morpheus Actually this is a different question: You can get the edges in PushRelabel.G, then [See here](https://stackoverflow.com/questions/9600825/algorithm-to-find-min-cut-given-max-flow) – martin Dec 18 '18 at 20:10
  • @Martin, Thanks for the answer. I was able to do it by creating a function to see if the edges are in a cut or not and then applying BFS from the source node to get the s-t cut. – Morpheus Dec 20 '18 at 11:06