private static bool BreadthFirstSearch(GridFlowGraph graph, Vector3i[,] parent, out Vector3i sink) { int width = graph.Width; int height = graph.Height; Queue <Vector2i> queue = new Queue <Vector2i>(); bool[,] isVisited = new bool[width, height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (graph.Label[x, y] != GridFlowGraph.SOURCE) { continue; } queue.Enqueue(new Vector2i(x, y)); parent[x, y] = new Vector3i(x, y, -1); isVisited[x, y] = true; } } while (queue.Count != 0) { Vector2i u = queue.Dequeue(); for (int i = 0; i < 8; i++) { float residual = graph.Capacity[u.x, u.y, i] - graph.Flow[u.x, u.y, i]; if (residual <= 0) { continue; } int xi = u.x + D8.OFFSETS[i, 0]; int yi = u.y + D8.OFFSETS[i, 1]; if (xi < 0 || xi >= width) { continue; } if (yi < 0 || yi >= height) { continue; } if (isVisited[xi, yi]) { continue; } queue.Enqueue(new Vector2i(xi, yi)); parent[xi, yi] = new Vector3i(u.x, u.y, i); isVisited[xi, yi] = true; if (graph.Label[xi, yi] == GridFlowGraph.SINK) { sink = new Vector3i(xi, yi, -1); return(true); } } } sink = new Vector3i(-1, -1, -1); return(false); }
public void FoldFulkersonMaxFlow() { int width = 4; int height = 2; GridFlowGraph graph = new GridFlowGraph(width, height); graph.Label[0, 0] = GridFlowGraph.SOURCE; graph.Label[3, 0] = GridFlowGraph.SINK; graph.Capacity[0, 0, D8.RIGHT] = 16; graph.Capacity[0, 0, D8.RIGHT_TOP] = 13; graph.Capacity[1, 0, D8.TOP] = 10; graph.Capacity[1, 0, D8.RIGHT] = 12; graph.Capacity[1, 1, D8.BOTTOM] = 4; graph.Capacity[1, 1, D8.RIGHT] = 14; graph.Capacity[2, 0, D8.RIGHT] = 20; graph.Capacity[2, 0, D8.LEFT_TOP] = 9; graph.Capacity[2, 1, D8.BOTTOM] = 7; graph.Capacity[2, 1, D8.RIGHT_BOTTOM] = 4; graph.FordFulkersonMaxFlow(); Assert.AreEqual(23, graph.MaxFlow); Assert.AreEqual(GridFlowGraph.SOURCE, graph.Label[0, 0]); Assert.AreEqual(GridFlowGraph.SOURCE, graph.Label[1, 0]); Assert.AreEqual(GridFlowGraph.SOURCE, graph.Label[1, 1]); Assert.AreEqual(GridFlowGraph.SOURCE, graph.Label[2, 1]); Assert.AreEqual(GridFlowGraph.SINK, graph.Label[2, 0]); Assert.AreEqual(GridFlowGraph.SINK, graph.Label[3, 0]); float minCut = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (graph.Label[x, y] != GridFlowGraph.SOURCE) { continue; } for (int i = 0; i < 8; i++) { int xi = x + D8.OFFSETS[i, 0]; int yi = y + D8.OFFSETS[i, 1]; if (xi < 0 || xi > width - 1) { continue; } if (yi < 0 || yi > height - 1) { continue; } if (graph.Label[xi, yi] == GridFlowGraph.SINK) { minCut += graph.Flow[x, y, i]; } } } } Assert.AreEqual(23, minCut); }
internal static float MaxFlow(GridFlowGraph graph) { int width = graph.Width; int height = graph.Height; Vector3i[,] parent = new Vector3i[width, height]; float maxFlow = 0; Vector3i sink, v; while (BreadthFirstSearch(graph, parent, out sink)) { float flow = float.PositiveInfinity; v = sink; while (true) { Vector3i u = parent[v.x, v.y]; if (u.x == v.x && u.y == v.y) { throw new InvalidOperationException("Did not stop at source."); } float residual = graph.Capacity[u.x, u.y, u.z] - graph.Flow[u.x, u.y, u.z]; flow = Math.Min(flow, residual); if (graph.Label[u.x, u.y] == GridFlowGraph.SOURCE) { break; } else { v = u; } } if (flow == float.PositiveInfinity) { throw new InvalidOperationException("Could not find path flow."); } maxFlow += flow; v = sink; while (true) { Vector3i u = parent[v.x, v.y]; graph.Flow[u.x, u.y, u.z] += flow; graph.Flow[v.x, v.y, D8.OPPOSITES[u.z]] -= flow; if (graph.Label[u.x, u.y] == GridFlowGraph.SOURCE) { break; } else { v = u; } } } return(maxFlow); }