/** Finding vertex-disjoint paths * First version uses an auxiliary directed graph, and the edge-disjoint path finding from above. **/ // Find maximal set of vertex disjoint paths: returns the set of edges used. public static HashSet <Tuple <T, T> > VertexDisjointPaths <T>(this IGraphEdges <T> graph, T startVertex, T to) { if (Eq(startVertex, to) || graph.GetNeighbours(startVertex).Contains(to)) { return(new HashSet <Tuple <T, T> >()); } // So `startVertex` and `to` are not the same, and not neighbours // Build new directed graph; (t,0) is the "in" vertex for t, and (t,1) the "out" vertex. var transformed = new DirectedGraph <Tuple <T, int> >(); // Make "vertex edges", except start/finish foreach (var v in graph.DepthFirstSearch(startVertex)) { if (!Eq(startVertex, v) && !Eq(to, v)) { transformed.AddEdge(Tuple.Create(v, 0), Tuple.Create(v, 1)); } } // Link them all up foreach (var vout in graph.DepthFirstSearch(startVertex)) { foreach (var vin in graph.GetNeighbours(vout)) { transformed.AddEdge(Tuple.Create(vout, 1), Tuple.Create(vin, 0)); } } var transUsedEdges = transformed.EdgeDisjointPaths(Tuple.Create(startVertex, 1), Tuple.Create(to, 0)); // Check foreach (var edge in transUsedEdges) { if (Eq(edge.Item1.Item1, edge.Item2.Item1)) { if (edge.Item1.Item2 != 0 || edge.Item2.Item2 != 1) { throw new Exception(String.Format("Backwards edge: {0}", edge)); } } } return(new HashSet <Tuple <T, T> >( from edge in transUsedEdges where !Eq(edge.Item1.Item1, edge.Item2.Item1) select Tuple.Create(edge.Item1.Item1, edge.Item2.Item1))); }
/** Find edge-disjoint paths * Uses variant of Ford-Fulkerson algorithm. * See http://matthewdaws.github.io/Paths.html **/ // Actually does the work, and returns a Set of directed edges which form the paths private static HashSet <Tuple <T, T> > EdgeDisjointPaths <T>(this IGraphEdges <T> graph, T startVertex, T to) { // We store the current paths as simply a list of edges used. var currentPaths = new HashSet <Tuple <T, T> >(); if (Eq(startVertex, to)) { return(currentPaths); } var edges = new List <Tuple <T, T> >(); foreach (T vertex in graph.DepthFirstSearch(startVertex)) { edges.Add(from end in graph.GetNeighbours(vertex) select Tuple.Create(vertex, end)); } // Now apply the FF algorithm // Rather than build a new residual graph each time, keep a copy and update var residual = new DirectedGraph <T>(); residual.AddEdges(edges); while (true) { var newPath = residual.FindPath(startVertex, to); if (newPath.Count() == 0) { return(currentPaths); // End, so return number of paths found } // Now merge new paths into both residual graph and collection of used edges for (int index = 0; index < newPath.Count() - 1; ++index) { var edge = Tuple.Create(newPath[index], newPath[index + 1]); var revEdge = Tuple.Create(newPath[index + 1], newPath[index]); if (currentPaths.Contains(revEdge)) { currentPaths.Remove(revEdge); foreach (var e in graph.AsDirectedEdges(edge.Item1, edge.Item2)) { residual.RemoveEdge(e.Item1, e.Item2); } foreach (var e in graph.AsDirectedEdges(revEdge.Item1, revEdge.Item2)) { residual.AddEdge(e.Item1, e.Item2); } } else { currentPaths.Add(edge); foreach (var e in graph.AsDirectedEdges(edge.Item1, edge.Item2)) { residual.RemoveEdge(e.Item1, e.Item2); } residual.AddEdge(revEdge.Item1, revEdge.Item2); } } } }