📜  门|门 CS 1996 |第 74 题(1)

📅  最后修改于: 2023-12-03 15:28:47.584000             🧑  作者: Mango

「门|门 CS 1996 |第 74 题」题解

这是一道经典的图论题。给定一个 $n$ 个点,$m$ 条边的无向图,求与某个点 $s$ 的连通块大小最大的点 $t$ ,以及 $s$ 到 $t$ 的路径上边数的最小值。

思路

首先,我们可以直接 DFS 或 BFS 到 $s$ 所在的连通块。接下来,考虑此连通块的直径。直径即为图中最长的一条路径。不难发现,直径上的任意一点都可以作为 $t$ ,所以问题转化为求此连通块的直径。

求连通块的直径

计算连通块直径的方式有多种。这里介绍两种:

(1)任意选取一点 $u$,找到距离 $u$ 最远的一点 $v$;然后从 $v$ 开始找到距离 $v$ 最远的一点 $w$,$[v,w]$ 即为该连通块的直径。

(2)任意选取一个点 $u$,做一遍最短路,找到距离 $u$ 最远的一点 $v$,再以 $v$ 为起点进行一遍最短路,找到距离 $v$ 最远的一点 $w$,$[v,w]$ 即为该连通块的直径。

可以看出,方法(1)需要进行两次 DFS/BFS,时间复杂度为 $O(n)$,空间复杂度同样也是 $O(n)$。方法(2)只需要一次 DFS/BFS,但需要进行两次最短路查询,时间复杂度为 $O(m \log n)$,空间复杂度为 $O(n+m)$。

对于本题,可以使用方法(1)来求解。

求路径上边数的最小值

找到直径之后,我们还需要求出 $s$ 到直径任意一点的距离 $x$。这里的距离指边数。

我们以方法(1)为例,找到距离 $u$ 最远的点 $v$ 时,可以记录 $u$ 到 $v$ 的距离 $d_1$。从 $v$ 开始继续 DFS/BFS,找到距离 $v$ 最远的点 $w$ 时,可以记录 $v$ 到 $w$ 的距离 $d_2$。此时,$s$ 到直径上任意一点的距离即为 $\lfloor \frac{d_1+d_2}{2} \rfloor$。

代码

下面给出使用方法(1)的 Python 代码:

n, m, s = map(int, input().split())
adj = [[] for _ in range(n+1)]
for _ in range(m):
    u, v = map(int, input().split())
    adj[u].append(v)
    adj[v].append(u)

# DFS/BFS,寻找连通块
vis = [False] * (n+1)
vis[s] = True
stk = [s]
while stk:
    u = stk.pop()
    for v in adj[u]:
        if not vis[v]:
            vis[v] = True
            stk.append(v)

# 寻找连通块的直径
def dfs(u):
    submax, submaxv = 0, u
    for v in adj[u]:
        if not vis[v]:
            vis[v] = True
            d, t = dfs(v)
            if d > submax:
                submax, submaxv = d, t
    return submax+1, submaxv

vis = [False] * (n+1)
vis[s] = True
_, v = dfs(s)
vis = [False] * (n+1)
vis[v] = True
d1, u = dfs(v)
vis = [False] * (n+1)
vis[u] = True
d2, t = dfs(u)
print('%d %d' % (t, (d1+d2)//2))

时间复杂度为 $O(n+m)$,空间复杂度为 $O(n+m)$。