'''
每一个任务看做一条边,选中了边端点就是覆盖了一个任务,其实问题就是求
选最少的点数,然点能够覆盖所有边,就是二分图的最大匹配数
'''
import sys
from collections import deque
class Bipartite:
# 传入图中的边信息,所有边起点属于二分图part1, 所有边终点属于二分图part2, 输入数据自行保证没有重边
# 节点编号从1开始,所有节点编号为1, 2, 3, ..... max_node_num
def __init__(self, edges, max_node_num):
self.edges = edges
self.max_ndoe_num = max_node_num
self.__match_result = None
# 判断是否是二分图
def __isBipartite(self):
link = {}
node_color = {} # 节点的颜色
for a, b in self.edges:
if a not in link:
link[a] = []
if b not in link:
link[b] = []
link[a].append(b)
link[b].append(a)
node_color[a] = None
node_color[b] = None
def __dfs(cur, color) -> bool:
node_color[cur] = color
for next in link[cur]:
if node_color[next] == color:
return False
if node_color[next] is None:
if not __dfs(next, 1-color):
return False
return True
for node, color in node_color.items():
if color is None:
if not __dfs(node, 1):
return False
return True
# 是否是完全匹配
def isPerfectMatching(self):
if self.__match_result is None:
self.getMaxMatchNum()
if self.__match_result is None:
return False
all_nodes = set()
for a, b in self.edges:
all_nodes.add(a)
all_nodes.add(b)
# 最大匹配树乘以2就是匹配的节点数,匹配节点数和总结点数相同即为完全匹配
return self.__match_result[0] * 2 == len(all_nodes)
# 获取二分图最大匹配数量, 如果无法进行二分图匹配,返回None, 正常情况返回 (最大匹配数,最大匹配边列表)
# 节点编号从1开始,所有节点编号为1, 2, 3, ..... max_node_num
def getMaxMatchNum(self):
if not self.__isBipartite():
return None
max_node_num = self.max_ndoe_num
# 构建邻接表
link = [[] for _ in range(max_node_num + 1)]
for a, b in self.edges:
link[a].append(b)
part1_nodes = set([a for a, _ in self.edges]) # 所有属于part1中的节点
part2_node2matched = [None] * (max_node_num + 1) # 记录part2中节点和part1中哪个节点匹配
cnt = 0
for start_node in part1_nodes:
que = deque()
pre = [None] * (max_node_num + 1)
visited = [0] * (max_node_num + 1)
visited[start_node] = 1
que.append(start_node)
# BFS 找增广路
end_node = None
find_expand_path = False
while len(que) > 0:
cur = que.popleft()
for next in link[cur]:
if visited[next] == 0:
visited[next] = 1
pre[next] = cur
if part2_node2matched[next] is None:
# 找到非匹配点,也就是找到了增广路径
end_node = next
find_expand_path = True
break
else:
pre[part2_node2matched[next]] = next
que.append(part2_node2matched[next])
if find_expand_path:
flag = True
if end_node:
while pre[end_node] is not None:
if flag:
part2_node2matched[end_node] = pre[end_node]
end_node = pre[end_node]
flag = not flag
cnt += 1
# 读取匹配的边
match_edges = []
for node in range(1, max_node_num + 1):
if part2_node2matched[node] is not None:
match_edges.append((part2_node2matched[node], node))
self.__match_result = cnt, match_edges
return self.__match_result[0]
# 获取二分图最大匹配结果中的匹配边, 没有合法的二分图匹配方式返回None
def getMaxMatchEdges(self):
if self.__match_result is None:
self.getMaxMatchNum()
if self.__match_result is None:
return None
return [(a, b) for a, b in self.__match_result[1]]
while True:
s = sys.stdin.readline()
if s.strip() == '0':
break
try:
n, m, e_num = map(int, s.split())
except:
break
edges = []
for i in range(e_num):
s = sys.stdin.readline()
_, a, b = map(int, s.split())
if a == 0 or b == 0:
continue
a, b = a+1, b+n+1
edges.append((a, b))
print(Bipartite(edges, max_node_num=m+n).getMaxMatchNum())