-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDinicsMaximumFlow.cs
More file actions
110 lines (89 loc) · 3.75 KB
/
DinicsMaximumFlow.cs
File metadata and controls
110 lines (89 loc) · 3.75 KB
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
using System;
using System.Collections.Generic;
namespace AlgorithmsAndDataStructures.Algorithms.Graph.MaximumFlow;
public class DinicsMaximumFlow
{
#pragma warning disable CA1822 // Mark members as static
public int MaxFlow(int[][] flowNetwork, int source, int sink)
#pragma warning restore CA1822 // Mark members as static
{
if (flowNetwork is null) return default;
var residualGraph = new int[flowNetwork.GetLength(0)][];
var flow = 0;
var verticesLevels = new int[flowNetwork.Length];
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];
}
while (LeveledBfs(source, sink, residualGraph, verticesLevels, flowNetwork))
{
bool hasPath;
var startAt = new int[residualGraph.Length];
do
{
var visited = new bool[residualGraph.Length];
var delta = GetAugmentingPath(residualGraph, source, sink, visited, verticesLevels, startAt,
int.MaxValue);
hasPath = delta > 0;
if (hasPath)
flow += delta;
} while (hasPath);
}
return flow;
}
private static int GetAugmentingPath(IReadOnlyList<int[]> residualGraph, int current, int target,
IList<bool> visited, IReadOnlyList<int> verticesLevels, IList<int> startAt, int flow)
{
if (current == target) return flow;
for (var i = startAt[current]; i < residualGraph[current].Length; i++)
{
if (residualGraph[current][i] < 1) continue;
// We only go towards the target node, not backwards.
if (!visited[i] && verticesLevels[current] < verticesLevels[i])
{
visited[i] = true;
var delta = GetAugmentingPath(residualGraph, i, target, visited, verticesLevels, startAt,
Math.Min(flow, residualGraph[current][i]));
// We eliminate dead-end paths, since we can't achieve target nodes through them.
startAt[current] = i;
if (delta > 0)
{
// Reduce capacity of the nodes along the path with delta.
residualGraph[current][i] = residualGraph[current][i] - delta;
// Create back-node to allow flow undo.
residualGraph[i][current] = residualGraph[i][current] + delta;
return delta;
}
}
}
return 0;
}
private static bool LeveledBfs(int currentVertex, int targetVertex, IReadOnlyList<int[]> residualGraph,
int[] verticesLevels, IReadOnlyList<int[]> flowNetwork)
{
var queue = new Queue<int>();
Reset(verticesLevels);
verticesLevels[currentVertex] = 0;
queue.Enqueue(currentVertex);
while (queue.Count > 0)
{
var current = queue.Dequeue();
for (var i = 0; i < residualGraph[current].Length; i++)
{
if (residualGraph[current][i] < 1) continue;
// This checks that vertices is not visited and tha this is a forward edge.
if (verticesLevels[i] < 0 && flowNetwork[current][i] - residualGraph[current][i] >= 0)
{
verticesLevels[i] = verticesLevels[current] + 1;
queue.Enqueue(i);
}
}
}
return verticesLevels[targetVertex] > -1;
}
private static void Reset(IList<int> verticesLevels)
{
for (var i = 0; i < verticesLevels.Count; i++) verticesLevels[i] = -1;
}
}