# Compute the Maximal Matching in a Graph

## Introduction to Maximal Matching in a Graph

Maximal Matching in a graph is a set of edges such that no two edges share a common vertex and adding any more edges to the set would violate this condition. In other words, it is a set of pairwise non-adjacent edges, which means that no two edges in the set share an endpoint. The problem of computing the maximal matching in a graph has various applications in real-world scenarios, such as resource allocation, job assignment, and scheduling.

## Real-world Examples and Scenarios

Some real-world examples and scenarios where computing the maximal matching in a graph is used include:

Job assignment: In a company, there are multiple jobs and multiple employees. The company wants to assign jobs to the employees in such a way that each employee gets at most one job and each job is assigned to at most one employee. A graph can be created where vertices represent employees and jobs, and edges represent an employee being qualified for a specific job. A maximal matching in this graph would represent an optimal assignment of jobs to employees.

Resource allocation: In a cloud computing environment, there are multiple tasks and multiple servers. The tasks need to be allocated to the servers in such a way that each task is assigned to at most one server and each server can handle at most one task at a time. A graph can be created where vertices represent tasks and servers, and edges represent a task being compatible with a server. A maximal matching in this graph would represent an optimal allocation of tasks to servers.

## Real-world Scenario: College Admissions

A college wants to admit students to its various courses. There are a limited number of seats available in each course, and each student can only be admitted to one course. The college has a set of eligibility criteria for each course, and a student can apply for multiple courses if they meet the eligibility criteria. The college wants to admit as many students as possible, given the limited number of seats and the eligibility constraints.

### Problem Statement

Given a bipartite graph G = (A ∪ B, E), where A represents the set of students, B represents the set of courses, and E represents the set of edges between students and courses (indicating that a student is eligible for a particular course), find a maximal matching in the graph.

### Tying the Problem Statement to the Scenario

In the college admissions scenario, finding a maximal matching in the bipartite graph would result in an optimal assignment of students to courses, ensuring that the maximum number of students gets admitted.

### Solution

We can use the Hopcroft-Karp algorithm to find a maximal matching in the bipartite graph. The Hopcroft-Karp algorithm is an efficient algorithm for finding a maximum cardinality matching in a bipartite graph. The algorithm works by iteratively finding augmenting paths in the graph and updating the matching accordingly.

### Step by Step Solution

Create a bipartite graph with students as one set of vertices and courses as the other set of vertices. Connect an edge between a student and a course if the student is eligible for the course.

Initialize an empty matching.

Find an augmenting path in the current matching using a Breadth-First Search (BFS) and Depth-First Search (DFS) based approach.

If an augmenting path exists, update the matching by alternating the edges in the augmenting path.

Repeat steps 3 and 4 until no more augmenting paths can be found.

The final matching is a maximal matching in the graph.

### Code Solution

```
from collections import defaultdict
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = defaultdict(list)
def add_edge(self, u, v):
self.graph[u].append(v)
def bfs(self, match, dist):
queue = []
for u in range(self.V):
if match[u] == -1:
dist[u] = 0
queue.append(u)
else:
dist[u] = float("inf")
dist[-1] = float("inf")
while queue:
u = queue.pop(0)
if u != -1:
for v in self.graph[u]:
if dist[match[v]] == float("inf"):
dist[match[v]] = dist[u] + 1
queue.append(match[v])
return dist[-1] != float("inf")
def dfs(self, u, match, dist):
if u == -1:
return True
for v in self.graph[u]:
if dist[match[v]] == dist[u] + 1 and self.dfs(match[v], match, dist):
match[u] = v
match[v] = u
return True
dist[u] = float("inf")
return False
def hopcroft_karp(self):
match = [-1] * self.V
dist = [0] * self.V
matching = 0
while self.bfs(match, dist):
for u in range(self.V):
if match[u] == -1 and self.dfs(u, match, dist):
matching += 1
return matching
# Example graph (students and courses)
g = Graph(10)
g.add_edge(0, 5)
g.add_edge(0, 6)
g.add_edge(1, 5)
g.add_edge(1, 7)
g.add_edge(2, 6)
g.add_edge(2, 8)
g.add_edge(3, 7)
g.add_edge(3, 9)
g.add_edge(4, 8)
g.add_edge(4, 9)
print("Maximal Matching: ", g.hopcroft_karp())
```

### Code Explanation

In the code solution, we define a `Graph`

class with methods for adding edges and finding the maximal matching using the Hopcroft-Karp algorithm. The `bfs`

and `dfs`

methods are used to find augmenting paths in the graph, and the `hopcroft_karp`

method computes the maximal matching by iteratively finding and updating augmenting paths.

The example graph represents a college where there are 5 students (0, 1, 2, 3, 4) and 5 courses (5, 6, 7, 8, 9). The edges represent the eligibility of students for the courses. The `hopcroft_karp`

method returns the size of the maximal matching, which in this case is 5, meaning that all students can be admitted to a course.

### Solving Other Real-world Problems

The same solution can be applied to other real-world problems that involve bipartite graphs and require finding maximal matchings, such as job assignment or resource allocation. By creating a graph representing the problem's constraints and using the Hopcroft-Karp algorithm, an optimal solution can be found efficiently.