252 lines
7.7 KiB
Java
252 lines
7.7 KiB
Java
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 <miron.ficak@gmail.com>
|
||
*/
|
||
public class PushRelabel {
|
||
|
||
private final Queue<Vertex> queue = new ArrayDeque<Vertex>();
|
||
private final List<Vertex> vertices = new ArrayList<Vertex>();
|
||
|
||
private int relabelCounter;
|
||
private int n;
|
||
private Vertex source;
|
||
private Vertex sink;
|
||
|
||
private PushRelabel(Collection<Vertex> 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 <T> parameter of graph on which network is based
|
||
* @return the maximum flow
|
||
*/
|
||
public static <T extends Comparable<T>> Long getMaximumFlow(Map<Graph.Edge<T>, Long> edgesToCapacities, Graph.Vertex<T> source, Graph.Vertex<T> sink) {
|
||
if (edgesToCapacities == null)
|
||
throw new IllegalArgumentException("Graph is NULL.");
|
||
|
||
final Map<Graph.Vertex<T>, Vertex> vertexMap = new TreeMap<Graph.Vertex<T>, Vertex>();
|
||
for (Graph.Edge<T> 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<Graph.Edge<T>, Long> edgeWithCapacity : edgesToCapacities.entrySet()) {
|
||
final Graph.Edge<T> 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<Vertex> que = new ArrayDeque<Vertex>();
|
||
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<Edge> edges = new ArrayList<Edge>();
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|