📜  树上的动态规划 | 2套(1)

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

树上的动态规划 | 2套

简介

在树结构中,可以使用树上动态规划来处理一些问题。树上动态规划的核心思想是考虑以每个节点为根节点的子树中的最优解,并将这些最优解合并得到整棵树的最优解。本文将介绍树上动态规划的两个常见问题及解决方法。

问题1:树的直径

树的直径是指树中最长的路径,即树中任意两点间的路径中最长的那个。求解树的直径可以使用树上动态规划来解决。

解决方法

我们可以以任意一个节点为根节点,对以该节点为根节点的树进行遍历。对于当前遍历到的节点 $u$,可以使用动态规划维护以 $u$ 为根节点的树的深度和经过 $u$ 的直径。

根据定义,以 $u$ 为根节点的深度=$\max_{v\in son(u)} dep_v+1$;经过 $u$ 的直径=$\max_{v \in son(u)} dep_v + dep_{son(u)[i]\in son(u)}+1$。

具体来说,我们可以使用深度优先搜索(DFS)遍历以 $u$ 为根节点的子树,计算出对应的深度和经过 $u$ 的直径。

# Python代码:

def dfs(u, fa):
    dep[u] = 0 # 以u为根节点的树的深度
    dia[u] = 0 # 经过u的直径

    for v in G[u]: # 枚举u的儿子节点
        if v == fa:
            continue
        dfs(v, u)
        dep[u] = max(dep[u], dep[v] + 1)
        dia[u] = max(dia[u], dep[v] + dep[u]) # 更新经过u的直径
        for w in G[u]: # 枚举u的儿子节点,除了v
            if w == fa or w == v:
                continue
            dia[u] = max(dia[u], dep[v] + dep[w] + 2) # 更新经过u的直径

n = int(input()) # 树的节点数
G = [[] for i in range(n + 1)] # 存储树的邻接表
for i in range(n - 1):
    u, v = map(int, input().split())
    G[u].append(v)
    G[v].append(u)

dep = [0] * (n + 1) # 记录每个节点的深度
dia = [0] * (n + 1) # 记录经过每个节点的直径
dfs(1, 0) # 以1号节点为根节点进行DFS遍历
ans = max(dia) # 经过整棵树的直径即树的直径
print(ans)
复杂度分析

上述算法的时间复杂度为 $O(n)$,因为我们只需要遍历一次整颗树。空间复杂度为 $O(n)$,因为我们需要记录每个节点的深度和经过每个节点的直径。

问题2:树的重心

树的重心是指树中最小化子树最大值的节点。也就是说,在树中去掉重心所在的节点后,得到的各个连通块的最大子树的节点数最小。

解决方法

类似于求解树的直径,我们可以以任意一个节点为根节点,对以该节点为根节点的树进行遍历。对于当前遍历到的节点 $u$,可以使用动态规划维护以 $u$ 为根节点的树的子树大小和重心。

根据定义,以 $u$ 为根节点的子树大小=$\sum_{v\in son(u)} size_v+1$;以 $u$ 为根节点的树的重心是满足最小化 $\max_{v\in son(u)} (size_v+1)$ 的节点。

具体来说,我们可以使用深度优先搜索(DFS)遍历以 $u$ 为根节点的子树,计算出对应的子树大小和重心。

# Python代码:

def dfs(u, fa):
    size[u] = 1
    max_size[u] = 0

    for v in G[u]:
        if v == fa:
            continue
        dfs(v, u)
        size[u] += size[v]
        max_size[u] = max(max_size[u], size[v]) # 记录子树大小的最大值

    max_size[u] = max(max_size[u], n - size[u]) # 计算所有节点的子树大小的最大值
    if max_size[u] < max_size[root] or root == -1:
        root = u # 更新重心

n = int(input()) # 树的节点数
G = [[] for i in range(n + 1)] # 存储树的邻接表
for i in range(n - 1):
    u, v = map(int, input().split())
    G[u].append(v)
    G[v].append(u)

size = [0] * (n + 1) # 存储每个节点的子树大小
max_size = [0] * (n + 1) # 记录每个节点的子树大小的最大值
root = -1 # 记录重心
dfs(1, 0) # 以1号节点为根节点进行DFS遍历
print(root)
复杂度分析

上述算法的时间复杂度为 $O(n)$,因为我们只需要遍历一次整颗树。空间复杂度为 $O(n)$,因为我们需要记录每个节点的子树大小和子树大小的最大值。

结论

树上动态规划是一种解决树问题的有效方法,能够帮助我们在树结构中求解最优解、最短路径等问题。本文介绍了树上动态规划的两个常见问题及其解决方法,希望能够对读者有所帮助。