匈牙利算法
1.增广路径:看了好多对增广路径解释的都不清楚,这篇我觉的解释非常清楚。
https://blog.csdn.net/reid_zhang1993/article/details/44080167
2.最大匹配,最优匹配
最大是实现边路最多,最优是带加权的最大匹配。
3.讲解实例
a.DFS
注意return位置!!!
从A开始,visited置0, ,寻找增广路径,进入Y队列循环,E,if edge[A][E]==1 and visited[E]==0, if cy[E]==-1,匹配A、E,返回。
此时匹配为A、E
B,visited置0, 寻找增广路径,进入Y队列循环,E,if edge[B][E]==1 and visited[E]==0,不匹配。
F ,if edge[B][F]==1 and visited[E]==0,if cy[F]==-1匹配B、F。返回
此时匹配为A、E;B、F
C,visited置0, 寻找增广路径,进入Y队列循环,E,if edge[C][E]==1 and visited[E]==0,匹配。if cy[E]==-1,不满足,进入else,删掉A、E匹配,再递归,进入A的路径寻找。如果能找到,则返回匹配。
此时匹配为B、F;A、G;C、E
D,visited置0, 寻找增广路径,进入Y队列循环,E,if edge[D][E]==1 and visited[E]==0,不匹配
F,if edge[D][E]==1 and visited[E]==0,不匹配
G,if edge[D][E]==1 and visited[E]==0,匹配。if cy[G]==-1,不满足,进入else,删掉A、G匹配,再递归,进入A的路径寻找。如果能找到,则返回匹配。D-G-A-E-C-H,能找到增广路径,返回匹配。
M=[]
class DFS_hungary():
def __init__(self, nx, ny, edge, cx, cy, visited):
self.nx, self.ny=nx, ny
self.edge = edge
self.cx, self.cy=cx,cy
self.visited=visited
def max_match(self): #X序列循环匹配,从A-B-C-D...
res=0
for i in self.nx:
print(i)
if self.cx[i]==-1:
for key in self.ny: # 将visited置0表示未访问过
self.visited[key]=0
res+=self.path(i)
print(nx,ny)
print(edge)
print(cx,cy)
print(visited)
print('This is M:',M)
return res,M
def path(self, u): #寻找增广路径
for v in self.ny:
if self.edge[u][v] and (not self.visited[v]):
self.visited[v]=1
print('---')
print(self.visited)
print('---')
if self.cy[v]==-1:
self.cx[u] = v
self.cy[v] = u
M.append((u,v))
return 1
else:
M.remove((self.cy[v], v))
if self.path(self.cy[v]): #递归
self.cx[u] = v
self.cy[v] = u
M.append((u, v))
return 1
return 0
if __name__ == '__main__':
nx, ny = ['A', 'B', 'C', 'D'], ['E', 'F', 'G', 'H']
edge = {'A':{'E': 1, 'F': 0, 'G': 1, 'H':0}, 'B':{'E': 0, 'F': 1, 'G': 0, 'H':1}, 'C':{'E': 1, 'F': 0, 'G': 0, 'H':1}, 'D':{'E': 0, 'F': 0, 'G': 1, 'H':0}} # 1 表示可以匹配, 0 表示不能匹配
cx, cy = {'A':-1,'B':-1,'C':-1,'D':-1}, {'E':-1,'F':-1,'G':-1,'H':-1}
visited = {'E': 0, 'F': 0, 'G': 0,'H':0}
print (DFS_hungary(nx, ny, edge, cx, cy, visited).max_match())
'''
nx,ny表示X、Y序列
edge 表示匹配初始关系
cx、cy表示匹配过程中的配对关系
visited 表示是否访问过。???
在此过程中注意return的使用位置,很有讲究。
'''
'''
运行结果
A
---
{'F': 0, 'G': 0, 'H': 0, 'E': 1}
---
['A', 'B', 'C', 'D'] ['E', 'F', 'G', 'H']
{'D': {'F': 0, 'G': 1, 'H': 0, 'E': 0}, 'C': {'F': 0, 'G': 0, 'H': 1, 'E': 1}, 'A': {'F': 0, 'G': 1, 'H': 0, 'E': 1}, 'B': {'F': 1, 'G': 0, 'H': 1, 'E': 0}}
{'D': -1, 'C': -1, 'A': 'E', 'B': -1} {'F': -1, 'G': -1, 'H': -1, 'E': 'A'}
{'F': 0, 'G': 0, 'H': 0, 'E': 1}
This is M: [('A', 'E')]
B
---
{'F': 1, 'G': 0, 'H': 0, 'E': 0}
---
['A', 'B', 'C', 'D'] ['E', 'F', 'G', 'H']
{'D': {'F': 0, 'G': 1, 'H': 0, 'E': 0}, 'C': {'F': 0, 'G': 0, 'H': 1, 'E': 1}, 'A': {'F': 0, 'G': 1, 'H': 0, 'E': 1}, 'B': {'F': 1, 'G': 0, 'H': 1, 'E': 0}}
{'D': -1, 'C': -1, 'A': 'E', 'B': 'F'} {'F': 'B', 'G': -1, 'H': -1, 'E': 'A'}
{'F': 1, 'G': 0, 'H': 0, 'E': 0}
This is M: [('A', 'E'), ('B', 'F')]
C
---
{'F': 0, 'G': 0, 'H': 0, 'E': 1}
---
---
{'F': 0, 'G': 1, 'H': 0, 'E': 1}
---
['A', 'B', 'C', 'D'] ['E', 'F', 'G', 'H']
{'D': {'F': 0, 'G': 1, 'H': 0, 'E': 0}, 'C': {'F': 0, 'G': 0, 'H': 1, 'E': 1}, 'A': {'F': 0, 'G': 1, 'H': 0, 'E': 1}, 'B': {'F': 1, 'G': 0, 'H': 1, 'E': 0}}
{'D': -1, 'C': 'E', 'A': 'G', 'B': 'F'} {'F': 'B', 'G': 'A', 'H': -1, 'E': 'C'}
{'F': 0, 'G': 1, 'H': 0, 'E': 1}
This is M: [('B', 'F'), ('A', 'G'), ('C', 'E')]
D
---
{'F': 0, 'G': 1, 'H': 0, 'E': 0}
---
---
{'F': 0, 'G': 1, 'H': 0, 'E': 1}
---
---
{'F': 0, 'G': 1, 'H': 1, 'E': 1}
---
['A', 'B', 'C', 'D'] ['E', 'F', 'G', 'H']
{'D': {'F': 0, 'G': 1, 'H': 0, 'E': 0}, 'C': {'F': 0, 'G': 0, 'H': 1, 'E': 1}, 'A': {'F': 0, 'G': 1, 'H': 0, 'E': 1}, 'B': {'F': 1, 'G': 0, 'H': 1, 'E': 0}}
{'D': 'G', 'C': 'H', 'A': 'E', 'B': 'F'} {'F': 'B', 'G': 'D', 'H': 'C', 'E': 'A'}
{'F': 0, 'G': 1, 'H': 1, 'E': 1}
This is M: [('B', 'F'), ('C', 'H'), ('A', 'E'), ('D', 'G')]
(4, [('B', 'F'), ('C', 'H'), ('A', 'E'), ('D', 'G')])
'''
b.BFS
def BFS_hungary(g,Nx,Ny,Mx,My,chk,Q,prev):
res=0
for i in range(Nx):
print('第几轮:',i)
if Mx[i]==-1:
qs=qe=0
print('qs:',qs,'qe:',qe)
Q[qe]=i
print('qe:',qe,'Q:',Q)
qe+=1
print('qe:',qe)
prev[i]=-1
print('prev',prev)
flag=0
while(qs<qe and not flag):
u=Q[qs]
print('u:',u)
for v in range(Ny):
print('----------')
if flag:continue
if g[u][v] and chk[v]!=i:
chk[v]=i
print('v:',v,'chk:',chk)
Q[qe]=My[v]
print('qe',qe,'Q',Q)
qe+=1
print('qe',qe)
if My[v]>=0:
prev[My[v]]=u
print('prev',prev)
print('lala')
else:
flag=1
d,e=u,v
print('d:',d,'e:',e)
while d!=-1:
t=Mx[d]
print('t:',t)
Mx[d]=e
print('d:',d,'Mx',Mx)
My[e]=d
print('e:',e,'My:',My)
d=prev[d]
print('d:',d)
e=t
print('e:',e)
print('-----')
qs+=1
if Mx[i]!=-1:
res+=1
print('Mx:',Mx,'My:',My,'chk:',chk,'Q:',Q,'prev',prev)
print('----------------------------------------------------------------------------------')
return res
if __name__ == '__main__':
g=[[1,0,1,0],[0,1,0,1],[1,0,0,1],[0,0,1,0]]
Nx=4
Ny=4
Mx=[-1,-1,-1,-1]
My=[-1,-1,-1,-1]
chk=[-1,-1,-1,-1]
Q=[0 for i in range(5)]
#原代码
# Q=[0 for i in range(100)]
prev=[0,0,0,0]
print(BFS_hungary(g,Nx,Ny,Mx,My,chk,Q,prev))
'''
Mx 为以X序列对应的Y的序号
My 为以Y序列对应的X的序号
chk, v 该轮对应的匹配关系以Y为底
qe的数目代表v运行的次数,qe=1,代表v循环了一次。
qe=0 qe=1 qe=2
Q[ u, My[v], My[v], 0 , 0]
prev[,,,]代表是否访问过 -1,,-1,-1,-1 代表此前没有访问过,2 代表访问过两次。有两个X访问。
'''
'''
运行结果:
第几轮: 0
qs: 0 qe: 0
qe: 0 Q: [0, 0, 0, 0, 0]
qe: 1
prev [-1, 0, 0, 0]
u: 0
----------
v: 0 chk: [0, -1, -1, -1]
qe 1 Q [0, -1, 0, 0, 0]
qe 2
d: 0 e: 0
t: -1
d: 0 Mx [0, -1, -1, -1]
e: 0 My: [0, -1, -1, -1]
d: -1
e: -1
-----
----------
----------
----------
Mx: [0, -1, -1, -1] My: [0, -1, -1, -1] chk: [0, -1, -1, -1] Q: [0, -1, 0, 0, 0] prev [-1, 0, 0, 0]
----------------------------------------------------------------------------------
第几轮: 1
qs: 0 qe: 0
qe: 0 Q: [1, -1, 0, 0, 0]
qe: 1
prev [-1, -1, 0, 0]
u: 1
----------
-----
----------
v: 1 chk: [0, 1, -1, -1]
qe 1 Q [1, -1, 0, 0, 0]
qe 2
d: 1 e: 1
t: -1
d: 1 Mx [0, 1, -1, -1]
e: 1 My: [0, 1, -1, -1]
d: -1
e: -1
-----
----------
----------
Mx: [0, 1, -1, -1] My: [0, 1, -1, -1] chk: [0, 1, -1, -1] Q: [1, -1, 0, 0, 0] prev [-1, -1, 0, 0]
----------------------------------------------------------------------------------
第几轮: 2
qs: 0 qe: 0
qe: 0 Q: [2, -1, 0, 0, 0]
qe: 1
prev [-1, -1, -1, 0]
u: 2
----------
v: 0 chk: [2, 1, -1, -1]
qe 1 Q [2, 0, 0, 0, 0]
qe 2
prev [2, -1, -1, 0]
lala
-----
----------
-----
----------
-----
----------
v: 3 chk: [2, 1, -1, 2]
qe 2 Q [2, 0, -1, 0, 0]
qe 3
d: 2 e: 3
t: -1
d: 2 Mx [0, 1, 3, -1]
e: 3 My: [0, 1, -1, 2]
d: -1
e: -1
-----
Mx: [0, 1, 3, -1] My: [0, 1, -1, 2] chk: [2, 1, -1, 2] Q: [2, 0, -1, 0, 0] prev [2, -1, -1, 0]
----------------------------------------------------------------------------------
第几轮: 3
qs: 0 qe: 0
qe: 0 Q: [3, 0, -1, 0, 0]
qe: 1
prev [2, -1, -1, -1]
u: 3
----------
-----
----------
-----
----------
v: 2 chk: [2, 1, 3, 2]
qe 1 Q [3, -1, -1, 0, 0]
qe 2
d: 3 e: 2
t: -1
d: 3 Mx [0, 1, 3, 2]
e: 2 My: [0, 1, 3, 2]
d: -1
e: -1
-----
----------
Mx: [0, 1, 3, 2] My: [0, 1, 3, 2] chk: [2, 1, 3, 2] Q: [3, -1, -1, 0, 0] prev [2, -1, -1, -1]
----------------------------------------------------------------------------------
4
'''
c.BFS与DFS比较
https://www.cnblogs.com/wzl19981116/p/9397203.html
BFS节约时间,浪费空间。DFS节约空间,浪费时间。
代码部分来自
https://www.cnblogs.com/jamespei/p/5555734.html
感谢!