diff --git a/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h b/Headers/0003_Graph/0016_MaximumFlowEdmondsKarp.h new file mode 100644 index 0000000..6dfa090 --- /dev/null +++ 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 new file mode 100644 index 0000000..f597b9b --- /dev/null +++ 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/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..fc1492c --- /dev/null +++ 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 diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index 7bb7555..70c9814 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( @@ -36,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