重點:理解增廣路和取反
1. 匈牙利算法
- 求解目標:找到二分圖的最大匹配
- 整體思路:每一步尋找一條增廣路徑,取反
2. 關鍵步驟
二分圖的頂點分為左邊點集X和右邊點集Y,假定遍歷的點集是X。對於每一次迭代的點x_i,
- 搜索增廣路徑:遍歷x_i的鄰接節點y_j
- 如果y_j未匹配,則找到增廣路
- 如果y_j已匹配,則尋找y_j的匹配節點的增廣路徑(深搜或者廣搜)
- 取反:把增廣路徑中的已經匹配邊改成未匹配;未匹配的改成匹配
3. python代碼
算法輸入為字典形式的特殊鄰接表。特殊之處在於字典的鍵和值的頂點分別屬於二分圖的左右點集合。
深度搜索增廣路徑函數的參數中的visited_set的作用是避免重復訪問。
# 匈牙利算法(dfs)
class Hungarian:
def search_extend_path(self, l_node, adjoin_map, l_match, r_match, visited_set):
'''深度搜索增廣路徑'''
for r_node in adjoin_map[l_node]: # 鄰接節點
if r_node not in r_match.keys(): # 情況1: 未匹配, 則找到增廣路徑,取反
l_match[l_node] = r_node
r_match[r_node] = l_node
return True
else: # 情況2: 已匹配
next_l_node = r_match[r_node]
if next_l_node not in visited_set:
visited_set.add(next_l_node)
if self.search_extend_path(next_l_node, adjoin_map, l_match, r_match, visited_set): # 找到增廣路徑,取反
l_match[l_node] = r_node
r_match[r_node] = l_node
return True
return False
def run(self, adjoin_map):
'''
:param adjoin_map: {x_i: [y_j, y_k]}
:return:
'''
l_match, r_match = {}, {} # 存放匹配
for lNode in adjoin_map.keys():
self.search_extend_path(lNode, adjoin_map, l_match, r_match, set())
return l_match