From c15439e3ff79cf94e2e478e760ea684cd62d842d Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Sun, 9 Mar 2025 23:58:40 +0530 Subject: [PATCH 1/4] 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/4] 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/4] 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/4] 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