-
Notifications
You must be signed in to change notification settings - Fork 1
/
FordFulkerson.cs
120 lines (99 loc) · 4.23 KB
/
FordFulkerson.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System;
using System.Collections.Generic;
namespace AlgorithmsAndDataStructures.Algorithms.Graph.MaximumFlow
{
// Ford-Fulkerson is actually an approach and not the while algorithm, it doesn't specify the exact way to traverse a residual graph.
// Since this implementation uses BFS, it's technically Edmonds-Karp algorithm with complexity of O(V*E^2)
public class FordFulkerson
{
// Actually, flow network here is just a directed graph, though, it may as well be undirected.
#pragma warning disable CA1822 // Mark members as static
public int MaxFlow(int[][] flowNetwork)
#pragma warning restore CA1822 // Mark members as static
{
if (flowNetwork is null)
{
return default;
}
var residualGraph = new int[flowNetwork.GetLength(0)][];
var flow = 0;
for (var i = 0; i < residualGraph.Length; i++)
{
residualGraph[i] = new int[flowNetwork[i].Length];
for (var j = 0; j < residualGraph[i].Length; j++)
{
residualGraph[i][j] = flowNetwork[i][j];
}
}
bool hasPath;
var sink = flowNetwork.Length - 1;
const int source = 0;
do
{
// Do simple BFS and record the path from source to sink if such path exists.
var path = GetPath(residualGraph, source, sink);
hasPath = path != null;
if (hasPath)
{
// Get minimum available capacity in the augmenting path.
var delta = GetDelta(residualGraph, path, sink);
// Augment max flow with value of delta since we were able to find augmenting path.
flow += delta;
// Follow the path until we rich the source.
var currentVertex = sink;
var parent = path[currentVertex];
while (parent >= 0)
{
// Reduce capacity of the nodes along the path with delta.
residualGraph[parent][currentVertex] = residualGraph[parent][currentVertex] - delta;
// Create back-node to allow flow undo.
residualGraph[currentVertex][parent] = residualGraph[currentVertex][parent] + delta;
currentVertex = parent;
parent = path[currentVertex];
}
}
} while (hasPath);
return flow;
}
private static int GetDelta(IReadOnlyList<int[]> residualGraph, IReadOnlyList<int> path, int targetVertex)
{
var delta = int.MaxValue;
var currentVertex = targetVertex;
var parent = path[currentVertex];
while (parent >= 0)
{
delta = Math.Min(residualGraph[parent][currentVertex], delta);
currentVertex = parent;
parent = path[currentVertex];
}
return delta;
}
// Since BFS is used, we get the shortest path here.
private static int[] GetPath(IReadOnlyList<int[]> residualGraph, int currentVertex, int targetVertex)
{
var path = new int[residualGraph.Count];
var queue = new Queue<int>();
var visited = new bool[residualGraph.Count];
path[currentVertex] = -1;
visited[currentVertex] = true;
queue.Enqueue(currentVertex);
while (queue.Count > 0)
{
var current = queue.Dequeue();
for (var i = 0; i < residualGraph[current].Length; i++)
{
var adjacentVertexCapacity = residualGraph[current][i];
if (adjacentVertexCapacity > 0 && !visited[i])
{
path[i] = current;
visited[i] = true;
if (i == targetVertex)
return path;
queue.Enqueue(i);
}
}
}
return null;
}
}
}