📅  最后修改于: 2023-12-03 15:42:22.826000             🧑  作者: Mango
给定一个 $n$ 个点 $m$ 条边的有向图,边权都是 $1$。再给定 $m$ 个询问,每次询问给出两个点 $u,v$,要求你求出从 $u$ 出发到 $v$ 的路径中,经过的边至少在 $k$ 个询问中被询问过。
第一行两个整数 $n,m$,表示点数和边数。 接下来 $m$ 行,每行两个整数 $u,v$,表示一条从 $u$ 到 $v$ 的有向边。
接下来一行一个整数 $q$,表示询问个数。
接下来 $q$ 行,每行三个整数 $u,v,k$,表示一组询问。
输出一行,包含 $q$ 个整数,其中第 $i$ 个数表示第 $i$ 个询问的答案。
$1\leq n,m\leq 10^5$,
$1\leq q\leq 10^4$
6 7
1 2
1 3
2 3
2 4
3 4
3 5
4 6
3
1 6 1
1 6 2
1 6 3
1 1 2
本题要求路径经过的边至少在 $k$ 个询问中被询问过,在无权图中,最短路可以使用 BFS 算法求解,但是本题是带权图,因此不能使用 BFS。
我们可以使用二分图匹配求解,将每个询问所对应的路径中的所有边都添加到图中,如果存在一个二分图,它能够满足所匹配的每条边都至少被 $k$ 个询问经过,那么这个二分图所对应的匹配方案即为满足条件的。
而对于每个询问,则可以将其看作寻找 $u$ 到 $v$ 的一条链,链上要求经过的边数量至少为 $k$,这种问题可以使用二分答案求解。
因此,在本题中,我们可以使用二分答案 + 二分图匹配的方法来解决问题。
详细实现可以参考以下代码:
from collections import defaultdict
import bisect
# 统计每条边被包含在了哪些询问中
def get_edges_questions(edges, questions):
edges_questions = [set() for _ in range(len(edges))]
for i, (a, b) in enumerate(edges):
for j, (u, v, k) in enumerate(questions):
if a == u and b == v:
edges_questions[i].add(j)
return edges_questions
# 寻找是否有一个二分图,满足匹配的每条边都至少被 k 个询问所包含
def can_match_k(edges_questions, mid):
# s -> u, t -> v
for u, qs in enumerate(edges_questions):
for v in range(len(edges_questions)):
if u != v:
matches = [1 if q in qs or q in edges_questions[v] else 0 for q in range(len(questions))]
if sum(matches) >= mid and len(questions) - sum(matches) >= k:
bipartite.append((u, v))
return max_bipartite_match()
# 二分答案 + 二分图匹配
def binary_search_match(edges_questions, questions):
L = 0
R = len(questions)
result = []
while L < R:
mid = (L + R + 1) // 2
bipartite.clear()
if can_match_k(edges_questions, mid):
L = mid
else:
R = mid - 1
return result
# 构建二分图
def max_bipartite_match():
# 从左半部分的一个点出发,尽量将右半部分的点都匹配出去
# 这里可以选择使用广度优先搜索,也可以使用深度优先搜索
def dfs(u, matched):
for i in range(len(bipartite)):
v = bipartite[i][1]
if i not in matched and u == bipartite[i][0]:
matched.add(i)
if not (v in occupied and dfs(occupied[v], matched)):
occupied[v] = i
return True
return False
max_match = 0
occupied = {}
for i in range(len(edges_questions)):
if dfs(i, set()):
max_match += 1
return max_match
# 解析输入
n, m = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(m)]
q = int(input())
questions = [list(map(int, input().split())) for _ in range(q)]
# 获取每个边被包含在了哪些询问中
edges_questions = get_edges_questions(edges, questions)
# 二分答案 + 二分图匹配
binary_search_match(edges_questions, questions)
# 输出答案
for m in result:
print(m, end=' ')
代码中用到了一个 max_bipartite_match 函数,它是使用 DFS 实现的二分图匹配,但这个函数并没有在代码中实际使用到,二分图匹配的实现和应用都在 can_match_k 函数中进行了。
这段代码另外一个需要注意的地方是循环的条件,对于二分答案,要使用 while L < R
循环,而不是 while L <= R
,这是因为使用前者可以使答案向上取整,得到一个更加合理的答案。
最后,输出答案时,需要使用 print(m, end=' ')
,这是因为需要将每个答案之间加上一个空格,而 print
的默认行为是输出换行符,因此需要通过 end
参数来修改默认行为。