int M, N; //M, N分别表示左、右侧集合的元素数量
int Map[MAXM][MAXN]; //邻接矩阵存图
int p[MAXN]; //记录当前右侧元素所对应的左侧元素
bool vis[MAXN]; //记录右侧元素是否已被访问过
bool match(int i)
{
for (int j = 1; j <= N; ++j)
if (Map[i][j] && !vis[j]) //有边且未访问
{
vis[j] = true; //记录状态为访问过
if (p[j] == 0 || match(p[j])) //如果暂无匹配,或者原来匹配的左侧元素可以找到新的匹配
{
p[j] = i; //当前左侧元素成为当前右侧元素的新匹配
return true; //返回匹配成功
}
}
return false; //循环结束,仍未找到匹配,返回匹配失败
}
int Hungarian()
{
int cnt = 0;
for (int i = 1; i <= M; ++i)
{
memset(vis, 0, sizeof(vis)); //重置vis数组
if (match(i))
cnt++;
}
return cnt;
}
匈牙利算法用于无权二部图匹配,求二部图的最多连接边数。中心思想是:
1,从 i 点开始出发,试探有没有孤立点 j 可以连接。
1.1如果试探的 j 点是孤立点,那么直接连在一起,这样就新增加了一条边。新增加了一条匹配边之后,就退出match函数了。
1.2如果试探的 j 点已经不是孤立点了,这意味着,实际上被试探的 j 点和其他的点匹配了。这里要做的,就是试着把 j 点让给 i 点。如果 j 点的原有匹配点可以和 j 点以外的点匹配成功,那么,j 点就可以“脱身”出来,和 i 点形成新的组合。这样做,不会损失原来的组合(因为已经为落单的点找到匹配对象了),同时也增加了新的一条边。如果 j点的原有匹配点和 j 点以外的点不能匹配成功,那么即使拆分原有组合,把 j 和 i 组合在一起,也损失了一条原有边,即使把i,j连一起多了一条边,总体来看没有收益。
2,为什么每试探成功一次,就加一呢?因为每次即使试探成功,也只会增加一条匹配边。
我感觉论证不需要结合交替路就能理解。代码的意思和直接构造交替路还是有一点差别的。
KM算法
KM算法用于带权二部图匹配,求二部图最优(大)匹配权重和。中心思想是基于贪心构造子图,如果能在子图上找出完全匹配的路径,那么这份路径的权重和就是整个二部图最优匹配的权重和。子图找完全匹配路径,就可以用上面的匈牙利算法来解了。什么是完全匹配呢?就是二部图的少端点侧,所有点都找到了另一端的匹配点。万一原图中有些端点就是孤立的呢?别担心,可以看成边的权重为0。更具体的讲解看 从匈牙利算法到KM算法 - 知乎,讲得非常清晰了。
注意:KM算法需要从少端点侧出发试探,逐一匹配多端点侧。否则会死循环,失败。
class KM_Algorithm:
def __init__(self, Net):
#Net = [[3,4,6,4,9],[6,4,5,3,8],[7,5,3,4,2],[6,3,2,2,5],[8,4,5,4,7]] #NGraphic net
#self.Net = [[2,3,0,0],[0,4,4,0],[5,6,0,0],[0,0,7,0]]
#Net = [[1,1,0,0],[0,1,1,0],[1,1,0,0],[0,0,1,0]]
#Net = [[2,1,1],[3,2,1],[1,1,1]]
self.Net = Net
self.x_number = len(self.Net)
self.y_number = len(self.Net[0])
self.ux, self.uy = np.zeros(self.x_number, dtype=int), np.zeros(self.y_number, dtype=int) # variable for record path
self.lx, self.ly = np.zeros(self.x_number, dtype=int), np.zeros(self.y_number, dtype=int) # sign
self.result = np.zeros(self.y_number, dtype=int) #Store the final result
self.inc = 99999
def match(self, u):
# global inc
u = int(u)
self.ux[u] = 1 #record node that was explored
for v in range(self.y_number):
if self.uy[v] == 0:
t = self.lx[u] + self.ly[v] - self.Net[u][v]
if t == 0: #it means here is possible to find a pair
self.uy[v] = 1
if self.result[v] == -1 or self.match(self.result[v]):
self.result[v] = u
return 1
elif self.inc > t:
self.inc = t
return 0
def Kuh_Munkras(self):
#initialize lx,ly
for p in range(self.y_number):
self.ly[p] = 0
self.result[p] = -1
for p in range(self.x_number):
self.lx[p] = -999999 #minus infinite
for q in range(self.y_number):
if(self.lx[p] < self.Net[p][q]): #Choose the biggest value to lx[i]
self.lx[p] = self.Net[p][q]
#find the perfect match
for u in range(self.x_number):
while(1):
self.inc = 999999 # the minimum gap
self.coverUsed()
if(self.match(u)):
break
for i in range(self.x_number): #Change sign,and try again
if (self.ux[i]):
self.lx[i] -= self.inc
for j in range(self.y_number):
if (self.uy[j]):
self.ly[j] += self.inc
return self.result
def calculateSum(self):
sum = 0
for i in range(self.y_number):
sum += self.Net[self.result[i]][i]
return sum
def getResult(self):
return self.result
def set_Net(self,Net):
self.Net = Net
def coverUsed(self):
self.ux, self.uy = np.zeros(self.x_number, dtype=int), np.zeros(self.y_number, dtype=int) # variable for record path