From c15439e3ff79cf94e2e478e760ea684cd62d842d Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Sun, 9 Mar 2025 23:58:40 +0530 Subject: [PATCH 1/7] feature: max flow ford fulkerson added --- .../0015_MaximumFlowFordFulkerson.h | 29 +++++ .../0015_MaximumFlowFordFulkerson.cc | 119 ++++++++++++++++++ SourceCodes/0003_Graph/CMakeLists.txt | 1 + .../0015_MaximumFlowFordFulkersonTest.cc | 0 Tests/0003_Graph/CMakeLists.txt | 1 + 5 files changed, 150 insertions(+) create mode 100644 Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h create mode 100644 SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc create mode 100644 Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc diff --git a/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h b/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h new file mode 100644 index 0000000..ed29736 --- /dev/null +++ b/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +using namespace std; + +namespace MaximumFlowFordFulkerson +{ + class Graph + { + private: + int _noOfVertices; + int _source; + int _sink; + int _maximumFlow; + bool _flagParallelEdges; + vector> _adjMatrix; + vector> _residualGraph; + vector _parent; + vector _visited; + void ResolvePrallelEdges(); + void DepthFirstSearchVisit(int nodeU); + bool DepthFirstSearch(); + public: + void CreateGraph(int noOfVertices); + void PushDirectedEdge(int valueU, int valueV, int capacity); + int FindMaximumFlowFordFulkerson(); + }; +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc new file mode 100644 index 0000000..91e19d9 --- /dev/null +++ b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc @@ -0,0 +1,119 @@ +#include "../Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h" +using namespace std; + +namespace MaximumFlowFordFulkerson +{ + // Graph Private Member Methods + void Graph::ResolvePrallelEdges() + { + int countParallelEdges = 0; + for (int i = 0; i < this->_noOfVertices; i++) + { + for (int j = 0; j < this->_noOfVertices; j++) + { + if (this->_adjMatrix[i][j] != 0 && this->_adjMatrix[j][i] != 0) + { + countParallelEdges++; + } + } + } + + // As i->j and j->i both edges has been counted, actual count is count = count / 2 + countParallelEdges /= 2; + + this->_flagParallelEdges = countParallelEdges > 0 ? true : false; + + for (auto& edge : this->_adjMatrix) + { + edge.resize(this->_noOfVertices + countParallelEdges, 0); + } + int k = this->_noOfVertices; + this->_noOfVertices += countParallelEdges; + this->_visited.resize(this->_noOfVertices, false); + this->_parent.resize(this->_noOfVertices, -1); + this->_adjMatrix.resize(this->_noOfVertices, vector(this->_noOfVertices, 0)); + + for (int i = 0; i < this->_noOfVertices; i++) + { + for (int j = 0; j < this->_noOfVertices; j++) + { + if (this->_adjMatrix[i][j] != 0 && this->_adjMatrix[j][i] != 0) + { + this->_adjMatrix[i][k] = this->_adjMatrix[i][j]; + this->_adjMatrix[k][j] = this->_adjMatrix[i][j]; + this->_adjMatrix[i][j] = 0; + k++; + } + } + } + } + + void Graph::DepthFirstSearchVisit(int nodeU) + { + this->_visited[nodeU] = true; + for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++) + { + if (!this->_visited[nodeV] && this->_residualGraph[nodeU][nodeV] > 0) + { + this->_parent[nodeV] = nodeU; + this->DepthFirstSearchVisit(nodeV); + } + } + } + + bool Graph::DepthFirstSearch() + { + fill(this->_visited.begin(), this->_visited.end(), false); + fill(this->_parent.begin(), this->_parent.end(), -1); + this->DepthFirstSearchVisit(this->_source); + return this->_visited[this->_sink]; + } + + // Graph Public Member Methods + void Graph::CreateGraph(int noOfVertices) + { + this->_noOfVertices = noOfVertices; + this->_source = 0; + this->_sink = this->_noOfVertices - 1; + this->_maximumFlow = 0; + this->_flagParallelEdges = false; + this->_adjMatrix = vector>(this->_noOfVertices, vector(this->_noOfVertices, 0)); + this->_parent = vector(this->_noOfVertices, -1); + this->_visited = vector(this->_noOfVertices, false); + } + + void Graph::PushDirectedEdge(int valueU, int valueV, int capacity) + { + this->_adjMatrix[valueU][valueV] = capacity; + } + + int Graph::FindMaximumFlowFordFulkerson() + { + // Resolving all the parallel edges if present + this->ResolvePrallelEdges(); + this->_residualGraph = this->_adjMatrix; + int augmentedPathFlow = INT_MAX; + + // While there exists a path p from source to sink in the residual network G' + while (this->DepthFirstSearch()) + { + // Calculating c'(p) = min{ c'(u,v) : (u,v) is in p } + for (int nodeV = this->_sink; nodeV > this->_source; nodeV = this->_parent[nodeV]) + { + int nodeU = this->_parent[nodeV]; + augmentedPathFlow = min(augmentedPathFlow, this->_residualGraph[nodeU][nodeV]); + } + + for (int nodeV = this->_sink; nodeV > this->_source; nodeV = this->_parent[nodeV]) + { + int nodeU = this->_parent[nodeV]; + this->_residualGraph[nodeU][nodeV] -= augmentedPathFlow; + this->_residualGraph[nodeV][nodeU] += augmentedPathFlow; + } + this->_maximumFlow += augmentedPathFlow; + } + + return this->_maximumFlow; + } + +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/CMakeLists.txt b/SourceCodes/0003_Graph/CMakeLists.txt index 23daf43..12ca775 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -14,6 +14,7 @@ set(0003GRAPH_SOURCES 0012_DifferenceConstraintsShortestPaths.cc 0013_AllPairsShortestPathsFloydWarshall.cc 0014_AllPairsShortestPathsJohnson.cc + 0015_MaximumFlowFordFulkerson.cc ) diff --git a/Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc b/Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc new file mode 100644 index 0000000..e69de29 diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index 79b0409..7bb7555 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable( 0012_DifferenceConstraintsShortestPathsTest.cc 0013_AllPairsShortestPathsFloydWarshallTest.cc 0014_AllPairsShortestPathsJohnsonTest.cc + 0015_MaximumFlowFordFulkersonTest.cc ) target_link_libraries( From 211acd1c23a6b256c8cd4029fa11cd2612932438 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Tue, 11 Mar 2025 22:29:28 +0530 Subject: [PATCH 2/7] fix-test: optimization, prll edge test added --- .../0015_MaximumFlowFordFulkerson.h | 2 +- .../0015_MaximumFlowFordFulkerson.cc | 31 +++++---- .../0015_MaximumFlowFordFulkersonTest.cc | 64 +++++++++++++++++++ 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h b/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h index ed29736..743fea0 100644 --- a/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h +++ b/Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h @@ -18,7 +18,7 @@ namespace MaximumFlowFordFulkerson vector> _residualGraph; vector _parent; vector _visited; - void ResolvePrallelEdges(); + void ResolveAntiParallelEdges(); void DepthFirstSearchVisit(int nodeU); bool DepthFirstSearch(); public: diff --git a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc index 91e19d9..20d5f96 100644 --- a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc +++ b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc @@ -4,14 +4,14 @@ using namespace std; namespace MaximumFlowFordFulkerson { // Graph Private Member Methods - void Graph::ResolvePrallelEdges() + void Graph::ResolveAntiParallelEdges() { int countParallelEdges = 0; for (int i = 0; i < this->_noOfVertices; i++) { for (int j = 0; j < this->_noOfVertices; j++) { - if (this->_adjMatrix[i][j] != 0 && this->_adjMatrix[j][i] != 0) + if (this->_adjMatrix[i][j] > 0 && this->_adjMatrix[j][i] > 0) { countParallelEdges++; } @@ -21,23 +21,28 @@ namespace MaximumFlowFordFulkerson // As i->j and j->i both edges has been counted, actual count is count = count / 2 countParallelEdges /= 2; - this->_flagParallelEdges = countParallelEdges > 0 ? true : false; + this->_flagParallelEdges = countParallelEdges > 0; + if (!this->_flagParallelEdges) + { + return; + } + + int newNoOfVertices = this->_noOfVertices + countParallelEdges; for (auto& edge : this->_adjMatrix) { - edge.resize(this->_noOfVertices + countParallelEdges, 0); + edge.resize(newNoOfVertices, 0); } int k = this->_noOfVertices; - this->_noOfVertices += countParallelEdges; - this->_visited.resize(this->_noOfVertices, false); - this->_parent.resize(this->_noOfVertices, -1); - this->_adjMatrix.resize(this->_noOfVertices, vector(this->_noOfVertices, 0)); + this->_visited.resize(newNoOfVertices, false); + this->_parent.resize(newNoOfVertices, -1); + this->_adjMatrix.resize(newNoOfVertices, vector(newNoOfVertices, 0)); for (int i = 0; i < this->_noOfVertices; i++) { for (int j = 0; j < this->_noOfVertices; j++) { - if (this->_adjMatrix[i][j] != 0 && this->_adjMatrix[j][i] != 0) + if (this->_adjMatrix[i][j] > 0 && this->_adjMatrix[j][i] > 0) { this->_adjMatrix[i][k] = this->_adjMatrix[i][j]; this->_adjMatrix[k][j] = this->_adjMatrix[i][j]; @@ -46,6 +51,8 @@ namespace MaximumFlowFordFulkerson } } } + + this->_noOfVertices = newNoOfVertices; } void Graph::DepthFirstSearchVisit(int nodeU) @@ -90,13 +97,14 @@ namespace MaximumFlowFordFulkerson int Graph::FindMaximumFlowFordFulkerson() { // Resolving all the parallel edges if present - this->ResolvePrallelEdges(); + this->ResolveAntiParallelEdges(); this->_residualGraph = this->_adjMatrix; - int augmentedPathFlow = INT_MAX; // While there exists a path p from source to sink in the residual network G' while (this->DepthFirstSearch()) { + int augmentedPathFlow = INT_MAX; + // Calculating c'(p) = min{ c'(u,v) : (u,v) is in p } for (int nodeV = this->_sink; nodeV > this->_source; nodeV = this->_parent[nodeV]) { @@ -115,5 +123,4 @@ namespace MaximumFlowFordFulkerson return this->_maximumFlow; } - } \ No newline at end of file diff --git a/Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc b/Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc index e69de29..b96b2ef 100644 --- a/Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc +++ b/Tests/0003_Graph/0015_MaximumFlowFordFulkersonTest.cc @@ -0,0 +1,64 @@ +#include +#include "../Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" + +namespace MaximumFlowFordFulkerson +{ + UnitTestHelper unitTestHelper; + + TEST(MaximumFlowFordFulkerson, GraphWithNoParallelEdges) + { + // Arrange + Graph graph; + int noOfVertices = 6; + int expectedMaximumFlow = 23; + + + // Act + graph.CreateGraph(noOfVertices); + + graph.PushDirectedEdge(0, 1, 16); + graph.PushDirectedEdge(0, 2, 13); + graph.PushDirectedEdge(1, 3, 12); + graph.PushDirectedEdge(2, 1, 4); + graph.PushDirectedEdge(2, 4, 14); + graph.PushDirectedEdge(3, 2, 9); + graph.PushDirectedEdge(3, 5, 20); + graph.PushDirectedEdge(4, 3, 7); + graph.PushDirectedEdge(4, 5, 4); + + int actualMaximumFlow = graph.FindMaximumFlowFordFulkerson(); + + // Assert + ASSERT_EQ(expectedMaximumFlow, actualMaximumFlow); + } + + TEST(MaximumFlowFordFulkerson, GraphWithParallelEdges) + { + // Arrange + Graph graph; + int noOfVertices = 6; + int expectedMaximumFlow = 24; + + + // Act + graph.CreateGraph(noOfVertices); + + graph.PushDirectedEdge(0, 1, 16); + graph.PushDirectedEdge(0, 2, 13); + graph.PushDirectedEdge(1, 3, 12); + graph.PushDirectedEdge(1, 2, 6); + graph.PushDirectedEdge(2, 1, 10); + graph.PushDirectedEdge(2, 4, 14); + graph.PushDirectedEdge(2, 3, 2); + graph.PushDirectedEdge(3, 2, 11); + graph.PushDirectedEdge(3, 5, 20); + graph.PushDirectedEdge(4, 3, 7); + graph.PushDirectedEdge(4, 5, 4); + + int actualMaximumFlow = graph.FindMaximumFlowFordFulkerson(); + + // Assert + ASSERT_EQ(expectedMaximumFlow, actualMaximumFlow); + } +} \ No newline at end of file From 3f85008764ab19c97c2d9b8b8238ee5b1a471af9 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Wed, 12 Mar 2025 00:47:50 +0530 Subject: [PATCH 3/7] docs: added comments for max flow ford fulkerson --- .../0003_Graph/0015_MaximumFlowFordFulkerson.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc index 20d5f96..9c4cd57 100644 --- a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc +++ b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc @@ -23,12 +23,15 @@ namespace MaximumFlowFordFulkerson this->_flagParallelEdges = countParallelEdges > 0; + // If there are no anti-parallel edges, no need to modify the adjMatrix if (!this->_flagParallelEdges) { return; } int newNoOfVertices = this->_noOfVertices + countParallelEdges; + + // Modifying the adjMatrix for (auto& edge : this->_adjMatrix) { edge.resize(newNoOfVertices, 0); @@ -38,6 +41,7 @@ namespace MaximumFlowFordFulkerson this->_parent.resize(newNoOfVertices, -1); this->_adjMatrix.resize(newNoOfVertices, vector(newNoOfVertices, 0)); + // Removing the anti-parallel edges by adding new nodes for (int i = 0; i < this->_noOfVertices; i++) { for (int j = 0; j < this->_noOfVertices; j++) @@ -52,6 +56,7 @@ namespace MaximumFlowFordFulkerson } } + // Updating the total no of vertices after modifying the adjMatrix this->_noOfVertices = newNoOfVertices; } @@ -70,9 +75,16 @@ namespace MaximumFlowFordFulkerson bool Graph::DepthFirstSearch() { + // Resetting the visited values fill(this->_visited.begin(), this->_visited.end(), false); + + // Resetting the parent values fill(this->_parent.begin(), this->_parent.end(), -1); + + // Starting the DepthFirstSearch from the source vertex this->DepthFirstSearchVisit(this->_source); + + // Returning the visited value of the sink vertex, initially it was set to false return this->_visited[this->_sink]; } From efe64c29c1fec335d1a9cd23339981c67156489e Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Wed, 12 Mar 2025 00:51:27 +0530 Subject: [PATCH 4/7] fix: header fix for maxflow ford fulkerson --- SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc index 9c4cd57..b2090c8 100644 --- a/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc +++ b/SourceCodes/0003_Graph/0015_MaximumFlowFordFulkerson.cc @@ -1,4 +1,5 @@ #include "../Headers/0003_Graph/0015_MaximumFlowFordFulkerson.h" +#include using namespace std; namespace MaximumFlowFordFulkerson From dad6a8d6830c1feb688d2e6e6020fe8c055d4600 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Fri, 14 Mar 2025 15:35:55 +0530 Subject: [PATCH 5/7] core-feature: test cmake update, max flow edmondkarp init --- Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h | 0 SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc | 0 SourceCodes/0003_Graph/CMakeLists.txt | 1 + Tests/0001_Basics/CMakeLists.txt | 2 +- Tests/0002_Tree/CMakeLists.txt | 2 +- Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc | 0 Tests/0003_Graph/CMakeLists.txt | 1 + 7 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h create mode 100644 SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc create mode 100644 Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc diff --git a/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h b/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h new file mode 100644 index 0000000..e69de29 diff --git a/SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc b/SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc new file mode 100644 index 0000000..e69de29 diff --git a/SourceCodes/0003_Graph/CMakeLists.txt b/SourceCodes/0003_Graph/CMakeLists.txt index 12ca775..1accab0 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -15,6 +15,7 @@ set(0003GRAPH_SOURCES 0013_AllPairsShortestPathsFloydWarshall.cc 0014_AllPairsShortestPathsJohnson.cc 0015_MaximumFlowFordFulkerson.cc + 0016_MaximumFlowEdmondsKarp.cc ) diff --git a/Tests/0001_Basics/CMakeLists.txt b/Tests/0001_Basics/CMakeLists.txt index 54dfea4..9996f20 100644 --- a/Tests/0001_Basics/CMakeLists.txt +++ b/Tests/0001_Basics/CMakeLists.txt @@ -26,4 +26,4 @@ target_link_libraries( include(GoogleTest) -gtest_discover_tests(0001-Basics-Tests) \ No newline at end of file +gtest_discover_tests(0001-Basics-Tests DISCOVERY_TIMEOUT 30) \ No newline at end of file diff --git a/Tests/0002_Tree/CMakeLists.txt b/Tests/0002_Tree/CMakeLists.txt index e2810b3..d54d451 100644 --- a/Tests/0002_Tree/CMakeLists.txt +++ b/Tests/0002_Tree/CMakeLists.txt @@ -21,4 +21,4 @@ target_link_libraries( ) include(GoogleTest) -gtest_discover_tests(0002TreeTests) \ No newline at end of file +gtest_discover_tests(0002TreeTests DISCOVERY_TIMEOUT 30) \ No newline at end of file diff --git a/Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc b/Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc new file mode 100644 index 0000000..e69de29 diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index 7bb7555..8e7fc0a 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable( 0013_AllPairsShortestPathsFloydWarshallTest.cc 0014_AllPairsShortestPathsJohnsonTest.cc 0015_MaximumFlowFordFulkersonTest.cc + 0016_MaximumFlowEdmondsKarpTest.cc ) target_link_libraries( From 95cd62a5056a08180979fb24fb6f30c67a0e6188 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Fri, 14 Mar 2025 20:57:40 +0530 Subject: [PATCH 6/7] core: test cmake update --- Tests/0003_Graph/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index 8e7fc0a..70c9814 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -37,4 +37,4 @@ target_link_libraries( ) include(GoogleTest) -gtest_discover_tests(0003GraphTests) \ No newline at end of file +gtest_discover_tests(0003GraphTests DISCOVERY_TIMEOUT 30) \ No newline at end of file From 814e05f37fb50702a8575801c9cd00ac65abf119 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Sat, 15 Mar 2025 22:01:39 +0530 Subject: [PATCH 7/7] feature-test: max flow edmonds karp --- .../0003_Graph/0016_MaximumFlowEdmondsKarp.h | 28 ++++ .../0003_Graph/0016_MaximumFlowEdmondsKarp.cc | 144 ++++++++++++++++++ .../0016_MaximumFlowEdmondsKarpTest.cc | 64 ++++++++ 3 files changed, 236 insertions(+) diff --git a/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h b/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h index e69de29..6dfa090 100644 --- a/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h +++ b/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +using namespace std; + +namespace MaximumFlowEdmondsKarp +{ + class Graph + { + private: + int _noOfVertices; + int _source; + int _sink; + int _maximumFlow; + bool _flagParallelEdges; + vector> _adjMatrix; + vector> _residualGraph; + vector _parent; + vector _visited; + void ResolveAntiParallelEdges(); + bool BreadthFirstSearch(); + public: + void CreateGraph(int noOfVertices); + void PushDirectedEdge(int valueU, int valueV, int capacity); + int FindMaximumFlowEdmondsKarp(); + }; +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc b/SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc index e69de29..f597b9b 100644 --- a/SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc +++ b/SourceCodes/0003_Graph/0016_MaximumFlowEdmondsKarp.cc @@ -0,0 +1,144 @@ +#include "../Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h" +#include +#include +using namespace std; + +namespace MaximumFlowEdmondsKarp +{ + // Graph Private Member Methods + void Graph::ResolveAntiParallelEdges() + { + int countParallelEdges = 0; + for (int i = 0; i < this->_noOfVertices; i++) + { + for (int j = 0; j < this->_noOfVertices; j++) + { + if (this->_adjMatrix[i][j] > 0 && this->_adjMatrix[j][i] > 0) + { + countParallelEdges++; + } + } + } + + // As i->j and j->i both edges has been counted, actual count is count = count / 2 + countParallelEdges /= 2; + + this->_flagParallelEdges = countParallelEdges > 0; + + // If there are no anti-parallel edges, no need to modify the adjMatrix + if (!this->_flagParallelEdges) + { + return; + } + + int newNoOfVertices = this->_noOfVertices + countParallelEdges; + + // Modifying the adjMatrix + for (auto& edge : this->_adjMatrix) + { + edge.resize(newNoOfVertices, 0); + } + int k = this->_noOfVertices; + this->_visited.resize(newNoOfVertices, false); + this->_parent.resize(newNoOfVertices, -1); + this->_adjMatrix.resize(newNoOfVertices, vector(newNoOfVertices, 0)); + + // Removing the anti-parallel edges by adding new nodes + for (int i = 0; i < this->_noOfVertices; i++) + { + for (int j = 0; j < this->_noOfVertices; j++) + { + if (this->_adjMatrix[i][j] > 0 && this->_adjMatrix[j][i] > 0) + { + this->_adjMatrix[i][k] = this->_adjMatrix[i][j]; + this->_adjMatrix[k][j] = this->_adjMatrix[i][j]; + this->_adjMatrix[i][j] = 0; + k++; + } + } + } + + // Updating the total no of vertices after modifying the adjMatrix + this->_noOfVertices = newNoOfVertices; + } + + bool Graph::BreadthFirstSearch() + { + // Resetting the visited values + fill(this->_visited.begin(), this->_visited.end(), false); + + // Resetting the parent values + fill(this->_parent.begin(), this->_parent.end(), -1); + + queue nodeQueue; + nodeQueue.push(this->_source); + this->_visited[this->_source] = true; + + while (!nodeQueue.empty()) + { + int nodeU = nodeQueue.front(); + nodeQueue.pop(); + + for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++) + { + if (!this->_visited[nodeV] && this->_residualGraph[nodeU][nodeV] > 0) + { + this->_parent[nodeV] = nodeU; + this->_visited[nodeV] = true; + nodeQueue.push(nodeV); + } + } + } + + // Returning the visited value of the sink vertex, initially it was set to false + return this->_visited[this->_sink]; + } + + // Graph Public Member Methods + void Graph::CreateGraph(int noOfVertices) + { + this->_noOfVertices = noOfVertices; + this->_source = 0; + this->_sink = this->_noOfVertices - 1; + this->_maximumFlow = 0; + this->_flagParallelEdges = false; + this->_adjMatrix = vector>(this->_noOfVertices, vector(this->_noOfVertices, 0)); + this->_parent = vector(this->_noOfVertices, -1); + this->_visited = vector(this->_noOfVertices, false); + } + + void Graph::PushDirectedEdge(int valueU, int valueV, int capacity) + { + this->_adjMatrix[valueU][valueV] = capacity; + } + + int Graph::FindMaximumFlowEdmondsKarp() + { + // Resolving all the parallel edges if present + this->ResolveAntiParallelEdges(); + this->_residualGraph = this->_adjMatrix; + + // While there exists a path p from source to sink in the residual network G' + while (this->BreadthFirstSearch()) + { + int augmentedPathFlow = INT_MAX; + + // Calculating c'(p) = min{ c'(u,v) : (u,v) is in p } + for (int nodeV = this->_sink; nodeV > this->_source; nodeV = this->_parent[nodeV]) + { + int nodeU = this->_parent[nodeV]; + augmentedPathFlow = min(augmentedPathFlow, this->_residualGraph[nodeU][nodeV]); + } + + for (int nodeV = this->_sink; nodeV > this->_source; nodeV = this->_parent[nodeV]) + { + int nodeU = this->_parent[nodeV]; + this->_residualGraph[nodeU][nodeV] -= augmentedPathFlow; + this->_residualGraph[nodeV][nodeU] += augmentedPathFlow; + } + this->_maximumFlow += augmentedPathFlow; + } + + return this->_maximumFlow; + } +} \ No newline at end of file diff --git a/Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc b/Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc index e69de29..fc1492c 100644 --- a/Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc +++ b/Tests/0003_Graph/0016_MaximumFlowEdmondsKarpTest.cc @@ -0,0 +1,64 @@ +#include +#include "../Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" + +namespace MaximumFlowEdmondsKarp +{ + UnitTestHelper unitTestHelper; + + TEST(MaximumFlowEdmondsKarp, GraphWithNoParallelEdges) + { + // Arrange + Graph graph; + int noOfVertices = 6; + int expectedMaximumFlow = 23; + + + // Act + graph.CreateGraph(noOfVertices); + + graph.PushDirectedEdge(0, 1, 16); + graph.PushDirectedEdge(0, 2, 13); + graph.PushDirectedEdge(1, 3, 12); + graph.PushDirectedEdge(2, 1, 4); + graph.PushDirectedEdge(2, 4, 14); + graph.PushDirectedEdge(3, 2, 9); + graph.PushDirectedEdge(3, 5, 20); + graph.PushDirectedEdge(4, 3, 7); + graph.PushDirectedEdge(4, 5, 4); + + int actualMaximumFlow = graph.FindMaximumFlowEdmondsKarp(); + + // Assert + ASSERT_EQ(expectedMaximumFlow, actualMaximumFlow); + } + + TEST(MaximumFlowEdmondsKarp, GraphWithParallelEdges) + { + // Arrange + Graph graph; + int noOfVertices = 6; + int expectedMaximumFlow = 24; + + + // Act + graph.CreateGraph(noOfVertices); + + graph.PushDirectedEdge(0, 1, 16); + graph.PushDirectedEdge(0, 2, 13); + graph.PushDirectedEdge(1, 3, 12); + graph.PushDirectedEdge(1, 2, 6); + graph.PushDirectedEdge(2, 1, 10); + graph.PushDirectedEdge(2, 4, 14); + graph.PushDirectedEdge(2, 3, 2); + graph.PushDirectedEdge(3, 2, 11); + graph.PushDirectedEdge(3, 5, 20); + graph.PushDirectedEdge(4, 3, 7); + graph.PushDirectedEdge(4, 5, 4); + + int actualMaximumFlow = graph.FindMaximumFlowEdmondsKarp(); + + // Assert + ASSERT_EQ(expectedMaximumFlow, actualMaximumFlow); + } +} \ No newline at end of file