/** * @file * @brief Implementation of [Hopcroft–Karp](https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm) algorithm. * @details * The Hopcroft–Karp algorithm is an algorithm that takes as input a bipartite graph * and produces as output a maximum cardinality matching, it runs in O(E√V) time in worst case. * * ### Bipartite graph * A bipartite graph (or bigraph) is a graph whose vertices can be divided into two disjoint * and independent sets U and V such that every edge connects a vertex in U to one in V. * Vertex sets U and V are usually called the parts of the graph. * Equivalently, a bipartite graph is a graph that does not contain any odd-length cycles. * * ### Matching and Not-Matching edges * Given a matching M, edges that are part of matching are called Matching edges and edges that are not part * of M (or connect free nodes) are called Not-Matching edges. * * ### Maximum cardinality matching * Given a bipartite graphs G = ( V = ( X , Y ) , E ) whose partition has the parts X and Y, * with E denoting the edges of the graph, the goal is to find a matching with as many edges as possible. * Equivalently, a matching that covers as many vertices as possible. * * ###Augmenting paths : * Given a matching M, an augmenting path is an alternating path that starts from and ends on free vertices. * All single edge paths that start and end with free vertices are augmenting paths. * * * ###Concept : * A matching M is not maximum if there exists an augmenting path. It is also true other way, * i.e, a matching is maximum if no augmenting path exists. * * * ###Algorithm : * 1) Initialize the Maximal Matching M as empty. * 2) While there exists an Augmenting Path P * Remove matching edges of P from M and add not-matching edges of P to M * (This increases size of M by 1 as P starts and ends with a free vertex * i.e. a node that is not part of matching.) * 3) Return M. * * * * @author [Krishna Pal Deora](https://github.com/Krishnapal4050) * */ #include #include #include #include #include #include /** * @brief Represents Bipartite graph for * Hopcroft Karp implementation */ class BGraph { int m; ///< m is the number of vertices on left side of Bipartite Graph int n; ///< n is the number of vertices on right side of Bipartite Graph const int NIL; const int INF; std::vector >adj; ///< adj[u] stores adjacents of left side and 0 is used for dummy vertex std::vector pair_u; ///< value of vertex 'u' ranges from 1 to m std::vector pair_v; ///< value of vertex 'v' ranges from 1 to n std::vector dist; ///< dist represents the distance between vertex 'u' and vertex 'v' public: BGraph(); ///(m + 1); ///< pair_u[u] stores pair of u in matching on left side of Bipartite Graph.If u doesn't have any pair, then pair_u[u] is NIL pair_v = std::vector(n + 1); ///< pair_v[v] stores pair of v in matching on right side of Biparite Graph.If v doesn't have any pair, then pair_u[v] is NIL dist = std::vector(m + 1); ///< dist[u] stores distance of left side vertices // Initialize NIL as pair of all vertices for (int u = 0; u <= m; u++){ pair_u[u] = NIL; } for (int v = 0; v <= n; v++){ pair_v[v] = NIL; } int result = 0; ///< Initialize result // Keep updating the result while there is an augmenting path possible. while (bfs()) { // Find a free vertex to check for a matching for (int u = 1; u <= m; u++){ // If current vertex is free and there is // an augmenting path from current vertex // then increment the result if (pair_u[u] == NIL && dfs(u)){ result++; } } } return result; } /** * Function documentation * @brief This function checks for the possibility of augmented path availability * @returns `true` if there is an augmenting path available * @returns `false` if there is no augmenting path available */ bool BGraph::bfs() { std::queue q; ///< an integer queue for bfs // First layer of vertices (set distance as 0) for (int u = 1; u <= m; u++) { // If this is a free vertex, add it to queue if (pair_u[u] == NIL){ dist[u] = 0; ///< u is not matched so distance is 0 q.push(u); } else{ dist[u] = INF; ///< set distance as infinite so that this vertex is considered next time for availibility } } dist[NIL] = INF; ///< Initialize distance to NIL as infinite // q is going to contain vertices of left side only. while (!q.empty()) { int u = q.front(); ///< dequeue a vertex q.pop(); // If this node is not NIL and can provide a shorter path to NIL then if (dist[u] < dist[NIL]) { // Get all the adjacent vertices of the dequeued vertex u std::list::iterator it; for (it = adj[u].begin(); it != adj[u].end(); ++it) { int v = *it; // If pair of v is not considered so far i.e. (v, pair_v[v]) is not yet explored edge. if (dist[pair_v[v]] == INF) { dist[pair_v[v]] = dist[u] + 1; q.push(pair_v[v]); ///< Consider the pair and push it to queue } } } } return (dist[NIL] != INF); ///< If we could come back to NIL using alternating path of distinct vertices then there is an augmenting path available } /** * Function documentation * @brief This functions checks whether an augmenting path is available exists beginning with free vertex u * @param u represents position of vertex * @returns `true` if there is an augmenting path beginning with free vertex u * @returns `false` if there is no augmenting path beginning with free vertex u */ bool BGraph::dfs(int u) { if (u != NIL) { std::list::iterator it; for (it = adj[u].begin(); it != adj[u].end(); ++it) { int v = *it; ///< Adjacent vertex of u // Follow the distances set by BFS search if (dist[pair_v[v]] == dist[u] + 1) { // If dfs for pair of v also return true then new matching possible, store the matching if (dfs(pair_v[v]) == true) { pair_v[v] = u; pair_u[u] = v; return true; } } } dist[u] = INF; ///< If there is no augmenting path beginning with u then set distance to infinite. return false; } return true; } /** * Function documentation * @brief Default Constructor for initialization */ BGraph::BGraph():NIL(0),INF(INT_MAX) {} /** * Function documentation * @brief Constructor for initialization * @param m is the number of vertices on left side of Bipartite Graph * @param n is the number of vertices on right side of Bipartite Graph */ BGraph::BGraph(int m, int n):NIL(0),INF(INT_MAX) { this->m = m; this->n = n; adj = std::vector >(m + 1); } /** * Function documentation * @brief function to add edge from u to v * @param u is the position of first vertex * @param v is the position of second vertex */ void BGraph::addEdge(int u, int v) { adj[u].push_back(v); // Add v to u’s list. } /** * @brief Main function * @returns 0 on exit */ int main() { int v1 = 0, v2 = 0, e = 0; std::cin >> v1 >> v2 >> e; ///< vertices of left side, right side and edges BGraph g(v1, v2); // int u = 0, v = 0; for (int i = 0; i < e; ++i) { std::cin >> u >> v; g.addEdge(u, v); } int res = g.hopcroftKarpAlgorithm(); std::cout << "Maximum matching is " << res <<"\n"; return 0; }