package com.jwetherell.algorithms.graph; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.TreeMap; import com.jwetherell.algorithms.data_structures.Graph; /** * In mathematical optimization, the push–relabel algorithm (alternatively, preflow–push * algorithm) is an algorithm for computing maximum flows. The name "push–relabel" comes * from the two basic operations used in the algorithm. Throughout its execution, the * algorithm maintains a "preflow" and gradually converts it into a maximum flow by moving * flow locally between neighboring nodes using push operations under the guidance of an * admissible network maintained by relabel operations. * * https://en.wikipedia.org/wiki/Push%E2%80%93relabel_maximum_flow_algorithm * * @author Miron Ficak */ public class PushRelabel { private final Queue queue = new ArrayDeque(); private final List vertices = new ArrayList(); private int relabelCounter; private int n; private Vertex source; private Vertex sink; private PushRelabel(Collection vertices, Vertex source, Vertex sink) { this.vertices.addAll(vertices); this.source = source; this.sink = sink; this.n = vertices.size(); } /** * Computes maximum flow in flow network, using push-relabel algorithm with O(V^3) complexity. * * @param edgesToCapacities represents edges of network with capacities * @param source source of network * @param sink sink of network * @param parameter of graph on which network is based * @return the maximum flow */ public static > Long getMaximumFlow(Map, Long> edgesToCapacities, Graph.Vertex source, Graph.Vertex sink) { if (edgesToCapacities == null) throw new IllegalArgumentException("Graph is NULL."); final Map, Vertex> vertexMap = new TreeMap, Vertex>(); for (Graph.Edge edge : edgesToCapacities.keySet()) { vertexMap.put(edge.getFromVertex(), new Vertex()); vertexMap.put(edge.getToVertex(), new Vertex()); } final Vertex s = new Vertex(); // source vertexMap.put(source, s); final Vertex t = new Vertex(); // sink vertexMap.put(sink, t); final PushRelabel pushRelabel = new PushRelabel(vertexMap.values(), s, t); for (Map.Entry, Long> edgeWithCapacity : edgesToCapacities.entrySet()) { final Graph.Edge e = edgeWithCapacity.getKey(); addEdge( vertexMap.get(e.getFromVertex()), vertexMap.get(e.getToVertex()), edgeWithCapacity.getValue() ); } return pushRelabel.maxFlow(); } private static final void addEdge(Vertex from, Vertex to, long cost) { final int placeOfEdge = from.edges.indexOf(new Edge(from, to)); if (placeOfEdge == -1) { final Edge edge = new Edge(from, to, cost); final Edge revertedEdge = new Edge(to, from, 0); edge.revertedEdge = revertedEdge; revertedEdge.revertedEdge = edge; from.edges.add(edge); to.edges.add(revertedEdge); } else { from.edges.get(placeOfEdge).cost += cost; } } private final void recomputeHeight() { final Queue que = new ArrayDeque(); for (Vertex vertex : vertices) { vertex.visited = false; vertex.height = 2 * n; } sink.height = 0; source.height = n; source.visited = true; sink.visited = true; que.add(sink); while (!que.isEmpty()) { final Vertex act = que.poll(); for (Edge e : act.edges) { if (!e.to.visited && e.revertedEdge.cost > e.revertedEdge.flow) { e.to.height = act.height + 1; que.add(e.to); e.to.visited = true; } } } que.add(source); while (!que.isEmpty()) { final Vertex act = que.poll(); for (Edge e : act.edges) { if (!e.to.visited && e.revertedEdge.cost > e.revertedEdge.flow) { e.to.height = act.height + 1; que.add(e.to); e.to.visited = true; } } } } private final void init() { for (Edge e : source.edges) { e.flow = e.cost; e.revertedEdge.flow = -e.flow; e.to.excess += e.flow; if (e.to != source && e.to != sink) queue.add(e.to); } recomputeHeight(); relabelCounter = 0; } private static final void relabel(Vertex v) { int minimum = 0; for (Edge e : v.edges) { if (e.flow < e.cost) minimum = Math.min(minimum, e.to.height); } v.height = minimum + 1; } private final void push(Vertex u, Edge e) { final long delta = (u.excess < e.cost - e.flow) ? u.excess : e.cost - e.flow; e.flow += delta; e.revertedEdge.flow -= delta; u.excess -= delta; if (e.to.excess == 0 && e.to != source && e.to != sink) queue.add(e.to); e.to.excess += delta; } private final void discharge(Vertex u) { while (u.excess > 0) { if (u.currentEdge == u.edges.size()) { relabel(u); if ((++relabelCounter) == n) { recomputeHeight(); for (Vertex vertex : vertices) vertex.currentEdge = 0; relabelCounter = 0; } u.currentEdge = 0; } else { Edge e = u.edges.get(u.currentEdge); if (e.flow < e.cost && u.height == e.to.height + 1) push(u, e); else u.currentEdge++; } } } private final long maxFlow() { init(); while (!queue.isEmpty()) discharge(queue.poll()); return sink.excess; } private static final class Vertex { private final List edges = new ArrayList(); private boolean visited = false; private int height; private int currentEdge; private long excess; } private final static class Edge { private final Vertex from; private final Vertex to; private long cost; private long flow; private Edge revertedEdge; private Edge(Vertex from, Vertex to, long cost) { this.from = from; this.to = to; this.cost = cost; } private Edge(Vertex from, Vertex to) { this.from = from; this.to = to; } /** * {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Edge edge = (Edge) o; if (!from.equals(edge.from)) return false; return to.equals(edge.to); } /** * {@inheritDoc} */ @Override public int hashCode() { int result = from.hashCode(); result = 31 * result + to.hashCode(); return result; } } }