📅  最后修改于: 2023-12-03 15:28:49.132000             🧑  作者: Mango
本题为“门|门CS 2013”第31题,题目难度为普及/提高-。 题目描述如下:
给你一棵 $n$ 个节点的树,节点编号从 $1$ 到 $n$,现在你需要把这棵树划分为若干个连通块,使得每个连通块包含的节点编号都连续。求最少需要划分成多少个连通块。
本题有多种实现思路,以下将分别介绍它们。
这是一种比较暴力的做法,对于每个节点,从它的父节点开始往下走,一直到第一个叶子结点,这一整条路径都是一个连通块。 具体实现时,可以采用深度优先搜索(DFS)的方式遍历整棵树,每次遍历到一个叶子结点,就将路径上的点打上标记。 最后,遍历完整棵树后,将没有被标记的点记为一个新的连通块。 时间复杂度约为 $O(n\log n)$。
这是一种稍微高效一些的做法。思路是对于每个节点,求出以它为根的子树中,编号最小和最大的叶子结点,这个叶子结点当做一个连通块。 具体实现时,可以采用递归的方式,遍历整棵树,每次递归到一个节点,将它与它的孩子的编号最小和最大值进行比较,如果左右子树的编号范围均连续,则视为同一个连通块。 因为只需要递归遍历一遍树,所以时间复杂度约为 $O(n)$。
这是一种比较高效的做法。它的思路是对树进行序列化,横向遍历节点,根据节点的连续性,将它们划分为若干个连通块。 具体实现时,可以使用广度优先搜索(BFS)遍历树,遍历到每个节点时,将它与它的兄弟节点进行比较,如果兄弟节点编号连续,则视为同一个连通块。如果不连续,则将上一个连通块结束,并开始下一个连通块。最后,将没有被包含在任何一个连通块中的节点单独记为一个连通块即可。 时间复杂度约为 $O(n)$。
以下为实现思路三的代码:
from collections import deque
def solve():
n = int(input())
adj = [[] for _ in range(n + 1)]
for _ in range(n - 1):
u, v = map(int, input().split())
adj[u].append(v)
adj[v].append(u)
q = deque([(1, 0)]) # 层级遍历的队列
ans = 1 # 初始连通块数量
last = 1 # 记录上一次结束的位置
while q:
node, last_id = q.popleft()
for child in adj[node]:
if child == last_id:
continue
if child != last + 1:
ans += 1
last = node
q.append((child, node))
print(ans)
以上为Python 3的代码,采用了队列实现BFS遍历。时间复杂度为 $O(n)$ 的同时,空间复杂度为 $O(n)$。