📜  门|门CS 2011 |第 49 题(1)

📅  最后修改于: 2023-12-03 14:58:37.193000             🧑  作者: Mango

门|门CS 2011 |第 49 题
问题描述

给定一个 $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 算法

Tarjan 算法是一种基于深度优先搜索的算法,与DFS算法类似,对于每个未访问的节点,递归地进行DFS遍历,得到每个节点的访问时间戳。在搜索时记录节点的时间戳、能够到达的最小时间戳等信息,以确定当前节点是否为强连通分量的根节点。Tarjan算法的时间复杂度为 $O(N+M)$,其中 $N$ 表示节点数,$M$ 表示边数。

Kosaraju 算法

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两种求解方法,代码较为简单,需要注意细节问题。