2
2
3
3
4
4
class BlossomAuxData :
5
+ """Class to hold auxiliary data during the blossom algorithm's execution."""
6
+
5
7
def __init__ (self , queue : deque , parent : list [int ], base : list [int ],
6
8
in_blossom : list [bool ], match : list [int ], in_queue : list [bool ]):
7
9
self .queue = queue
@@ -11,18 +13,33 @@ def __init__(self, queue: deque, parent: list[int], base: list[int],
11
13
self .match = match
12
14
self .in_queue = in_queue
13
15
16
+
14
17
class BlossomData :
18
+ """Class to encapsulate data related to a blossom in the graph."""
19
+
15
20
def __init__ (self , aux_data : BlossomAuxData , u : int , v : int , lca : int ):
16
21
self .aux_data = aux_data
17
22
self .u = u
18
23
self .v = v
19
24
self .lca = lca
20
25
26
+
21
27
class EdmondsBlossomAlgorithm :
22
28
UNMATCHED = - 1 # Constant to represent unmatched vertices
23
29
24
30
@staticmethod
25
31
def maximum_matching (edges : list [list [int ]], vertex_count : int ) -> list [list [int ]]:
32
+ """
33
+ Finds the maximum matching in a graph using the Edmonds Blossom Algorithm.
34
+
35
+ Args:
36
+ edges: A list of edges represented as pairs of vertices.
37
+ vertex_count: The total number of vertices in the graph.
38
+
39
+ Returns:
40
+ A list of matched pairs in the form of a list of lists.
41
+ """
42
+ # Create an adjacency list for the graph
26
43
graph = [[] for _ in range (vertex_count )]
27
44
28
45
# Populate the graph with the edges
@@ -35,95 +52,114 @@ def maximum_matching(edges: list[list[int]], vertex_count: int) -> list[list[int
35
52
match = [EdmondsBlossomAlgorithm .UNMATCHED ] * vertex_count
36
53
parent = [EdmondsBlossomAlgorithm .UNMATCHED ] * vertex_count
37
54
base = list (range (vertex_count )) # Each vertex is its own base initially
38
- # Indicates if a vertex is part of a blossom
39
55
in_blossom = [False ] * vertex_count
40
56
in_queue = [False ] * vertex_count # Tracks vertices in the BFS queue
41
57
42
58
# Main logic for finding maximum matching
43
59
for u in range (vertex_count ):
60
+ # Only consider unmatched vertices
44
61
if match [u ] == EdmondsBlossomAlgorithm .UNMATCHED :
45
62
# BFS initialization
46
63
parent = [EdmondsBlossomAlgorithm .UNMATCHED ] * vertex_count
47
64
base = list (range (vertex_count ))
48
65
in_blossom = [False ] * vertex_count
49
66
in_queue = [False ] * vertex_count
50
67
51
- queue = deque ([u ])
68
+ queue = deque ([u ]) # Start BFS from the unmatched vertex
52
69
in_queue [u ] = True
53
70
54
71
augmenting_path_found = False
55
72
56
73
# BFS to find augmenting paths
57
74
while queue and not augmenting_path_found :
58
- current = queue .popleft ()
59
- for y in graph [current ]:
75
+ current = queue .popleft () # Get the current vertex
76
+ for y in graph [current ]: # Explore adjacent vertices
77
+ # Skip if we're looking at the current match
60
78
if match [current ] == y :
61
- # Skip if we are
62
- # looking at the same edge
63
- # as the current match
64
79
continue
65
80
66
- if base [current ] == base [y ]:
67
- continue # Avoid self-loops
81
+ if base [current ] == base [y ]: # Avoid self-loops
82
+ continue
68
83
69
84
if parent [y ] == EdmondsBlossomAlgorithm .UNMATCHED :
70
- # Case 1: y is unmatched, we've found an augmenting path
85
+ # Case 1: y is unmatched; we've found an augmenting path
71
86
if match [y ] == EdmondsBlossomAlgorithm .UNMATCHED :
72
- parent [y ] = current
87
+ parent [y ] = current # Update the parent
73
88
augmenting_path_found = True
74
89
# Augment along this path
75
- (EdmondsBlossomAlgorithm
76
- .update_matching (match , parent , y ))
90
+ EdmondsBlossomAlgorithm .update_matching (match ,
91
+ parent ,
92
+ y )
77
93
break
78
94
79
- # Case 2: y is matched, add y's match to the queue
95
+ # Case 2: y is matched; add y's match to the queue
80
96
z = match [y ]
81
97
parent [y ] = current
82
98
parent [z ] = y
83
- if not in_queue [z ]:
99
+ if not in_queue [z ]: # If z is not already in the queue
84
100
queue .append (z )
85
101
in_queue [z ] = True
86
102
else :
87
103
# Case 3: Both current and y have a parent;
88
104
# check for a cycle/blossom
89
105
base_u = EdmondsBlossomAlgorithm .find_base (base ,
90
- parent , current , y )
106
+ parent ,
107
+ current ,
108
+ y )
91
109
if base_u != EdmondsBlossomAlgorithm .UNMATCHED :
92
110
EdmondsBlossomAlgorithm .contract_blossom (BlossomData (
93
- BlossomAuxData (queue ,
94
- parent ,
95
- base ,
96
- in_blossom ,
97
- match ,
98
- in_queue ),
111
+ BlossomAuxData (queue , parent ,
112
+ base , in_blossom ,
113
+ match , in_queue ),
99
114
current , y , base_u ))
100
115
101
116
# Create result list of matched pairs
102
117
matching_result = []
103
118
for v in range (vertex_count ):
119
+ # Ensure pairs are unique
104
120
if match [v ] != EdmondsBlossomAlgorithm .UNMATCHED and v < match [v ]:
105
121
matching_result .append ([v , match [v ]])
106
122
107
123
return matching_result
108
124
109
125
@staticmethod
110
126
def update_matching (match : list [int ], parent : list [int ], u : int ):
127
+ """
128
+ Updates the matching based on the augmenting path found.
129
+
130
+ Args:
131
+ match: The current match list.
132
+ parent: The parent list from BFS traversal.
133
+ u: The vertex where the augmenting path ends.
134
+ """
111
135
while u != EdmondsBlossomAlgorithm .UNMATCHED :
112
- v = parent [u ]
113
- next_match = match [v ]
114
- match [v ] = u
115
- match [u ] = v
116
- u = next_match
136
+ v = parent [u ] # Get the parent vertex
137
+ next_match = match [v ] # Store the next match
138
+ match [v ] = u # Update match for v
139
+ match [u ] = v # Update match for u
140
+ u = next_match # Move to the next vertex
117
141
118
142
@staticmethod
119
143
def find_base (base : list [int ], parent : list [int ], u : int , v : int ) -> int :
144
+ """
145
+ Finds the base of the blossom.
146
+
147
+ Args:
148
+ base: The base array for each vertex.
149
+ parent: The parent array from BFS.
150
+ u: One endpoint of the blossom.
151
+ v: The other endpoint of the blossom.
152
+
153
+ Returns:
154
+ The lowest common ancestor of u and v in the blossom.
155
+ """
120
156
visited = [False ] * len (base )
121
157
122
158
# Mark ancestors of u
123
159
current_u = u
124
160
while True :
125
161
current_u = base [current_u ]
126
- visited [current_u ] = True
162
+ visited [current_u ] = True # Mark this base as visited
127
163
if parent [current_u ] == EdmondsBlossomAlgorithm .UNMATCHED :
128
164
break
129
165
current_u = parent [current_u ]
@@ -132,23 +168,32 @@ def find_base(base: list[int], parent: list[int], u: int, v: int) -> int:
132
168
current_v = v
133
169
while True :
134
170
current_v = base [current_v ]
135
- if visited [current_v ]:
171
+ if visited [current_v ]: # Check if we've already visited this base
136
172
return current_v
137
173
current_v = parent [current_v ]
138
174
139
175
@staticmethod
140
176
def contract_blossom (blossom_data : BlossomData ):
177
+ """
178
+ Contracts a blossom found during the matching process.
179
+
180
+ Args:
181
+ blossom_data: The data related to the blossom to be contracted.
182
+ """
183
+ # Mark vertices in the blossom
141
184
for x in range (blossom_data .u ,
142
185
blossom_data .aux_data .base [blossom_data .u ] != blossom_data .lca ):
143
186
base_x = blossom_data .aux_data .base [x ]
144
187
match_base_x = blossom_data .aux_data .base [blossom_data .aux_data .match [x ]]
188
+ # Mark the base as in a blossom
145
189
blossom_data .aux_data .in_blossom [base_x ] = True
146
190
blossom_data .aux_data .in_blossom [match_base_x ] = True
147
191
148
192
for x in range (blossom_data .v ,
149
193
blossom_data .aux_data .base [blossom_data .v ] != blossom_data .lca ):
150
194
base_x = blossom_data .aux_data .base [x ]
151
195
match_base_x = blossom_data .aux_data .base [blossom_data .aux_data .match [x ]]
196
+ # Mark the base as in a blossom
152
197
blossom_data .aux_data .in_blossom [base_x ] = True
153
198
blossom_data .aux_data .in_blossom [match_base_x ] = True
154
199
0 commit comments