Я реализую алгоритм Форда-Фалкерсона, но у меня возникла проблема с обновлением графика после фазы увеличения. Думаю, моя структура данных не упрощает эту задачу.
Для представления графика я использую это:
private Map<Vertex, ArrayList<Edge>> outgoingEdges;
То есть я связываю в каждой вершине свой список исходящих ребер.
Чтобы управлять обратными ребрами, я связал «противоположное» ребро для каждого ребра в графе.
Любое предложение приветствуется.
public class FF {
/**
* Associates each Vertex with his list of outgoing edges
*/
private Map<Vertex, ArrayList<Edge>> outgoingEdges;
public FF() {
outgoingEdges = new HashMap<Vertex, ArrayList<Edge>>();
}
/**
* Returns the nodes of the graph
*/
public Collection<Vertex> getNodes() {
return outgoingEdges.keySet();
}
/**
* Returns the outgoing edges of a node
*/
public Collection<Edge> getIncidentEdges(Vertex v) {
return outgoingEdges.get(v);
}
/**
* Adds a new edge to the graph
*/
public void insertEdge(Vertex source, Vertex destination, float capacity) throws Exception {
if (!(outgoingEdges.containsKey(source) && outgoingEdges.containsKey(destination)))
throw new Exception("Unable to add the edge");
Edge e = new Edge(source, destination, capacity);
Edge opposite = new Edge(destination, source, capacity);
outgoingEdges.get(source).add(e);
outgoingEdges.get(destination).add(opposite);
e.setOpposite(opposite);
opposite.setOpposite(e);
}
/**
* Adds a new node to the graph
*/
public void insertNode(Vertex v) {
if (!outgoingEdges.containsKey(v))
outgoingEdges.put(v, new ArrayList<Edge>());
}
/**
* Ford-Fulkerson algorithm
*
* @return max flow
*/
public int fordFulkerson(Vertex source, Vertex destination) {
List<Edge> path = new ArrayList<Edge>();
int maxFlow = 0;
while(bfs(source, destination, path)) {
// finds the bottleneck
float minCap = bottleneck(path);
// updates the maxFlow
maxFlow += minCap;
// updates the graph <-- this updates only the local list path, not the graph!
for(Edge e : path) {
try {
e.addFlow(minCap);
e.getOpposite().addFlow(-minCap);
} catch (Exception e1) {
e1.printStackTrace();
}
}
path.clear();
}
return maxFlow;
}
/**
* @param Path of which we have to find the bottleneck
* @return bottleneck of the path
*/
private float bottleneck(List<Edge> path) {
float min = Integer.MAX_VALUE;
for(Edge e : path) {
float capacity = e.getCapacity();
if(capacity <= min) {
min = capacity;
}
}
return min;
}
/**
* BFS to obtain a path from the source to the destination
*
* @param source
* @param destination
* @param path
* @return
*/
private boolean bfs(Vertex source, Vertex destination, List<Edge> path) {
Queue<Vertex> queue = new LinkedList<Vertex>();
List<Vertex> visited = new ArrayList<Vertex>(); // list of visited vertexes
queue.add(source);
//source.setVisited(true);
visited.add(source);
while(!queue.isEmpty()) {
Vertex d = queue.remove();
if(!d.equals(destination)) {
ArrayList<Edge> d_outgoingEdges = outgoingEdges.get(d);
for(Edge e : d_outgoingEdges) {
if(e.getCapacity() - e.getFlow() > 0) { // there is still available flow
Vertex u = e.getDestination();
if(!visited.contains(u)) {
//u.setVisited(true);
visited.add(u);
queue.add(u);
path.add(e);
}
}
}
}
}
if(visited.contains(destination)) {
return true;
}
return false;
}
}
Грань
public class Edge {
private Vertex source;
private Vertex destination;
private float flow;
private final float capacity;
private Edge opposite;
public Edge(Vertex source, Vertex destination, float capacity) {
this.source = source;
this.destination = destination;
this.capacity = capacity;
}
public Edge getOpposite() {
return opposite;
}
public void setOpposite(Edge e) {
opposite = e;
}
public void setSource(Vertex v) {
source = v;
}
public void setDestination(Vertex v) {
destination = v;
}
public void addFlow(float f) throws Exception {
if(flow == capacity) {
throw new Exception("Unable to add flow");
}
flow += f;
}
public Vertex getSource() {
return source;
}
public Vertex getDestination() {
return destination;
}
public float getFlow() {
return flow;
}
public float getCapacity() {
return capacity;
}
public boolean equals(Object o) {
Edge e = (Edge)o;
return e.getSource().equals(this.getSource()) && e.getDestination().equals(this.getDestination());
}
}
Вершина
public class Vertex {
private String label;
public Vertex(String label) {
this.label = label;
}
public boolean isVisited() {
return visited;
}
public String getLabel() {
return label;
}
public boolean equals(Object o) {
Vertex v = (Vertex)o;
return v.getLabel().equals(this.getLabel());
}
}
this updates only the local list...
? Этого не должно быть, так как кажется, что ссылки ребер используются везде, а новые ребра не создаются. Если есть сомнения, вставьте методprintDebugInfo
, который печатает все ребра и их потоки, и вызывайте этот метод после каждого шага обновления (используя небольшой график, чтобы вы могли легко проверить вывод с ручкой+бумагой) - person Marco13   schedule 30.11.2014node.setVisited(false)
на всех узлах. Если он работает вечно, вывод отладки (как упоминалось выше) уже может быть полезен. Если нет, попробуйте создать stackoverflow.com/help/mcve (с основным методом и небольшим примером графика). - person Marco13   schedule 30.11.2014