📅  最后修改于: 2023-12-03 14:58:37.193000             🧑  作者: Mango
给定一个 $N$ 个点 $M$ 条边的有向图,求这个图里有多少个强连通分量的大小不超过 $K$。
第一行包含 $3$ 个正整数 $N$,$M$,$K$。
接下来 $M$ 行,每行包含两个整数 $a$,$b$,表示存在一条 $a$ 到 $b$ 的有向边。
输出一个整数,表示满足条件的强连通分量个数。
3 3 3
1 2
2 3
3 1
1
本题可以使用Tarjan算法或Kosaraju算法求出有向图的强连通分量,统计符合要求的强连通分量的数量即可。
Tarjan 算法是一种基于深度优先搜索的算法,与DFS算法类似,对于每个未访问的节点,递归地进行DFS遍历,得到每个节点的访问时间戳。在搜索时记录节点的时间戳、能够到达的最小时间戳等信息,以确定当前节点是否为强连通分量的根节点。Tarjan算法的时间复杂度为 $O(N+M)$,其中 $N$ 表示节点数,$M$ 表示边数。
Kosaraju 算法也是一种基于深度优先搜索的算法,与Tarjan 算法类似,只是在第二次深度优先遍历时对反向边进行遍历。该算法也可以求出有向图的强连通分量,时间复杂度为 $O(N+M)$。
下面是一份参考代码,使用Tarjan算法求解强连通分量的个数,并统计符合要求的个数。
def tarjan(u: int):
low[u] = dfn[u] = dfns
dfns += 1
stack.append(u)
in_stack[u] = True
for v in g[u]:
if dfn[v] == -1:
tarjan(v)
low[u] = min(low[u], low[v])
elif in_stack[v]:
low[u] = min(low[u], dfn[v])
if low[u] == dfn[u]:
cnt = 0
while True:
v = stack.pop()
in_stack[v] = False
cnt += 1
if v == u:
break
if cnt <= K:
ans += 1
N, M, K = map(int, input().split())
g, dfn, low = [[] for _ in range(N)], [-1] * N, [-1] * N
dfns, ans = 0, 0
stack, in_stack = [], [False] * N
for _ in range(M):
a, b = map(int, input().split())
g[a - 1].append(b - 1)
for i in range(N):
if dfn[i] == -1:
tarjan(i)
print(ans)
本题是一道典型的强连通分量问题,需要掌握Tarjan和Kosaraju两种求解方法,代码较为简单,需要注意细节问题。