Skip to content

Commit 20e53ed

Browse files
committed
更新「二分图最大匹配」相关内容
1 parent 79c30ef commit 20e53ed

File tree

1 file changed

+277
-0
lines changed

1 file changed

+277
-0
lines changed
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
## 1. 二分图最大匹配简介
2+
3+
> **二分图最大匹配(Maximum Bipartite Matching)**:图论中的一个重要问题。在二分图中,我们需要找到最大的匹配数,即最多可以有多少对顶点之间形成匹配。
4+
5+
- **二分图**:图中的顶点可以被分成两个独立的集合,使得每条边的两个端点分别属于这两个集合。
6+
- **匹配**:一组边的集合,其中任意两条边都没有共同的顶点。
7+
- **最大匹配**:包含边数最多的匹配。
8+
9+
### 1.1 应用场景
10+
11+
二分图最大匹配在实际应用中有广泛的应用:
12+
13+
1. **任务分配**:将任务分配给工人,每个工人只能完成一个任务
14+
2. **婚姻匹配**:将男生和女生进行配对
15+
3. **网络流问题**:可以转化为最大流问题求解
16+
4. **资源分配**:将资源分配给需求方
17+
5. **学生选课**:将学生与课程进行匹配
18+
6. **网络路由**:将数据包与可用路径进行匹配
19+
20+
### 1.2 优化方法
21+
22+
1. **使用邻接表**:对于稀疏图,使用邻接表可以显著减少空间复杂度
23+
2. **双向搜索**:同时从左右两侧进行搜索,可以减少搜索次数
24+
3. **预处理**:对图进行预处理,去除不可能形成匹配的边
25+
4. **贪心匹配**:先进行贪心匹配,减少后续搜索的复杂度
26+
5. **并行处理**:对于大规模图,可以使用并行算法提高效率
27+
28+
## 2. 匈牙利算法
29+
30+
### 2.1 匈牙利算法基本思想
31+
32+
匈牙利算法(Hungarian Algorithm)是求解二分图最大匹配的经典算法。其基本思想是:
33+
34+
1. 从左侧集合中任选一个未匹配的点开始
35+
2. 尝试寻找增广路径
36+
3. 如果找到增广路径,则更新匹配
37+
4. 重复以上步骤直到无法找到增广路径
38+
39+
### 2.2 匈牙利算法实现代码
40+
41+
```python
42+
def max_bipartite_matching(graph, left_size, right_size):
43+
# 初始化匹配数组
44+
match_right = [-1] * right_size
45+
result = 0
46+
47+
# 对左侧每个顶点尝试匹配
48+
for left in range(left_size):
49+
# 记录右侧顶点是否被访问过
50+
visited = [False] * right_size
51+
52+
# 如果找到增广路径,则匹配数加1
53+
if find_augmenting_path(graph, left, visited, match_right):
54+
result += 1
55+
56+
return result
57+
58+
def find_augmenting_path(graph, left, visited, match_right):
59+
# 遍历右侧所有顶点
60+
for right in range(len(graph[left])):
61+
# 如果存在边且右侧顶点未被访问
62+
if graph[left][right] and not visited[right]:
63+
visited[right] = True
64+
65+
# 如果右侧顶点未匹配,或者可以找到新的匹配
66+
if match_right[right] == -1 or find_augmenting_path(graph, match_right[right], visited, match_right):
67+
match_right[right] = left
68+
return True
69+
70+
return False
71+
```
72+
73+
### 2.3 匈牙利算法时间复杂度
74+
75+
- 匈牙利算法的时间复杂度为 O(VE),其中 V 是顶点数,E 是边数
76+
- 使用邻接矩阵存储图时,空间复杂度为 O(V²)
77+
- 使用邻接表存储图时,空间复杂度为 O(V + E)
78+
79+
80+
## 3. Hopcroft-Karp 算法
81+
82+
### 3.1 Hopcroft-Karp 算法基本思想
83+
84+
Hopcroft-Karp 算法是求解二分图最大匹配的一个更高效的算法,时间复杂度为 O(√VE)。其基本思想是:
85+
86+
1. 同时寻找多条不相交的增广路径
87+
2. 使用 BFS 分层,然后使用 DFS 寻找增广路径
88+
3. 每次迭代可以找到多条增广路径
89+
90+
91+
### 3.2 Hopcroft-Karp 算法实现代码
92+
93+
```python
94+
from collections import deque
95+
96+
def hopcroft_karp(graph, left_size, right_size):
97+
# 初始化匹配数组
98+
match_left = [-1] * left_size
99+
match_right = [-1] * right_size
100+
result = 0
101+
102+
while True:
103+
# 使用 BFS 寻找增广路径
104+
dist = [-1] * left_size
105+
queue = deque()
106+
107+
# 将未匹配的左侧顶点加入队列
108+
for i in range(left_size):
109+
if match_left[i] == -1:
110+
dist[i] = 0
111+
queue.append(i)
112+
113+
# BFS 分层
114+
while queue:
115+
left = queue.popleft()
116+
for right in graph[left]:
117+
if match_right[right] == -1:
118+
# 找到增广路径
119+
break
120+
if dist[match_right[right]] == -1:
121+
dist[match_right[right]] = dist[left] + 1
122+
queue.append(match_right[right])
123+
124+
# 使用 DFS 寻找增广路径
125+
def dfs(left):
126+
for right in graph[left]:
127+
if match_right[right] == -1 or \
128+
(dist[match_right[right]] == dist[left] + 1 and \
129+
dfs(match_right[right])):
130+
match_left[left] = right
131+
match_right[right] = left
132+
return True
133+
return False
134+
135+
# 尝试为每个未匹配的左侧顶点寻找增广路径
136+
found = False
137+
for i in range(left_size):
138+
if match_left[i] == -1 and dfs(i):
139+
found = True
140+
result += 1
141+
142+
if not found:
143+
break
144+
145+
return result
146+
```
147+
148+
### 3.3 Hopcroft-Karp 算法复杂度
149+
150+
- **时间复杂度**:O(√VE),其中 V 是顶点数,E 是边数
151+
- **空间复杂度**:O(V + E)
152+
- **优点**
153+
1. 比匈牙利算法更高效
154+
2. 适合处理大规模图
155+
3. 可以并行化实现
156+
- **缺点**
157+
1. 实现相对复杂
158+
2. 常数因子较大
159+
3. 对于小规模图可能不如匈牙利算法
160+
161+
### 3.4 Hopcroft-Karp 算法优化
162+
163+
1. **双向 BFS**:同时从左右两侧进行 BFS,减少搜索空间
164+
2. **动态分层**:根据当前匹配状态动态调整分层策略
165+
3. **预处理**:使用贪心算法进行初始匹配
166+
4. **并行化**:利用多线程或分布式计算提高效率
167+
168+
## 4. 网络流算法
169+
170+
### 4.1 网络流算法实现步骤
171+
172+
二分图最大匹配问题可以转化为最大流问题来求解。具体步骤如下:
173+
174+
1. 添加源点和汇点
175+
2. 将二分图转化为网络流图
176+
3. 使用最大流算法求解
177+
178+
### 4.2 网络流算法实现代码
179+
180+
```python
181+
from collections import defaultdict
182+
183+
def max_flow_bipartite_matching(graph, left_size, right_size):
184+
# 构建网络流图
185+
flow_graph = defaultdict(dict)
186+
source = left_size + right_size
187+
sink = source + 1
188+
189+
# 添加源点到左侧顶点的边
190+
for i in range(left_size):
191+
flow_graph[source][i] = 1
192+
flow_graph[i][source] = 0
193+
194+
# 添加右侧顶点到汇点的边
195+
for i in range(right_size):
196+
flow_graph[left_size + i][sink] = 1
197+
flow_graph[sink][left_size + i] = 0
198+
199+
# 添加二分图中的边
200+
for i in range(left_size):
201+
for j in graph[i]:
202+
flow_graph[i][left_size + j] = 1
203+
flow_graph[left_size + j][i] = 0
204+
205+
# 使用 Ford-Fulkerson 算法求解最大流
206+
def bfs():
207+
parent = [-1] * (sink + 1)
208+
queue = deque([source])
209+
parent[source] = -2
210+
211+
while queue:
212+
u = queue.popleft()
213+
for v, capacity in flow_graph[u].items():
214+
if parent[v] == -1 and capacity > 0:
215+
parent[v] = u
216+
if v == sink:
217+
return parent
218+
queue.append(v)
219+
return None
220+
221+
def ford_fulkerson():
222+
max_flow = 0
223+
while True:
224+
parent = bfs()
225+
if not parent:
226+
break
227+
228+
# 找到增广路径上的最小容量
229+
v = sink
230+
min_capacity = float('inf')
231+
while v != source:
232+
u = parent[v]
233+
min_capacity = min(min_capacity, flow_graph[u][v])
234+
v = u
235+
236+
# 更新流量
237+
v = sink
238+
while v != source:
239+
u = parent[v]
240+
flow_graph[u][v] -= min_capacity
241+
flow_graph[v][u] += min_capacity
242+
v = u
243+
244+
max_flow += min_capacity
245+
246+
return max_flow
247+
248+
return ford_fulkerson()
249+
```
250+
251+
### 4.3 网络流算法复杂度
252+
253+
- **时间复杂度**
254+
1. Ford-Fulkerson 算法:O(VE²)
255+
2. Dinic 算法:O(V²E)
256+
3. ISAP 算法:O(V²E)
257+
- **空间复杂度**:O(V + E)
258+
259+
## 5. 算法复杂度分析
260+
261+
1. **匈牙利算法**
262+
- 时间复杂度:O(VE)
263+
- 优点:实现简单,容易理解
264+
- 缺点:对于大规模图效率较低
265+
- 适用场景:小规模图,需要快速实现
266+
267+
2. **Hopcroft-Karp 算法**
268+
- 时间复杂度:O(√VE)
269+
- 优点:效率更高,适合大规模图
270+
- 缺点:实现相对复杂
271+
- 适用场景:大规模图,需要高效算法
272+
273+
3. **网络流算法**
274+
- 时间复杂度:O(VE²) 或 O(V²E)
275+
- 优点:可以处理更复杂的问题,如带权匹配
276+
- 缺点:实现复杂,常数较大
277+
- 适用场景:带权匹配,复杂约束条件

0 commit comments

Comments
 (0)