如何找到树中被最多叶到叶路径经过的节点?dp-on-tree解法求助
解题思路
这个问题的核心是计算每个节点被多少条叶到叶路径经过,最终取数值最大的对应节点即可,不需要复杂的换根DP,仅通过两次线性遍历就能实现。
首先明确:树中任意一条叶到叶路径经过节点u的充要条件是,路径的两个端点分别位于删除u后形成的不同连通块中。基于这个性质可以快速推导每个节点的路径计数。
算法步骤
- 统计整棵树的总叶子节点数M:叶子节点定义为度数为1的节点,若M≤1说明不存在叶到叶路径,直接返回任意节点即可。
- 任选根节点(比如1号节点),做一次后序DFS预处理
dp数组:dp[u]表示以u为根的子树内的叶子节点总数。- 若u本身是叶子节点,
dp[u] = 1 - 否则
dp[u]等于所有子节点的dp值之和
- 若u本身是叶子节点,
- 遍历每个节点u,计算经过它的路径总数:
- 删除u后,树会拆分为k个连通块(k等于u的度数),每个连通块的叶子数分别为
s_1, s_2, ...s_k - 对于u的子节点对应的连通块,
s_i = dp[v](v为u的子节点) - 对于u的父节点方向的连通块,
s_i = M - dp[u] - 经过u的路径数 = 总叶到叶路径数 - 所有连通块内部的叶到叶路径数
- 删除u后,树会拆分为k个连通块(k等于u的度数),每个连通块的叶子数分别为
- 遍历所有节点取路径数最大的节点即为答案。
核心公式
总叶到叶路径数为 total = M*(M-1)//2
每个节点u的路径计数:ans[u] = total - sum( s_i*(s_i-1)//2 for all s_i )
你给出的示例中M=5,total=10,节点2删除后连通块的叶子数分别为1、1、1、2,sum值为0+0+0+1=1,所以ans[2]=10-1=9,和你给出的路径数完全匹配。
代码实现
import sys sys.setrecursionlimit(1 << 25) def main(): input = sys.stdin.read data = input().split() idx = 0 N = int(data[idx]) idx += 1 adj = [[] for _ in range(N + 1)] for _ in range(N - 1): u = int(data[idx]) v = int(data[idx + 1]) adj[u].append(v) adj[v].append(u) idx += 2 # 统计总叶子数 M = 0 for i in range(1, N + 1): if len(adj[i]) == 1: M += 1 if M <= 1: print(1) return dp = [0] * (N + 1) # 后序DFS计算dp数组 def dfs(u, parent): if len(adj[u]) == 1: dp[u] = 1 return cnt = 0 for v in adj[u]: if v == parent: continue dfs(v, u) cnt += dp[v] dp[u] = cnt dfs(1, -1) total = M * (M - 1) // 2 max_cnt = -1 res_node = 1 # 计算每个节点的路径数 for u in range(1, N + 1): sum_same = 0 for v in adj[u]: # 判断v是子节点还是父节点 if dp[v] < dp[u] or (len(adj[v]) == 1 and dp[v] == 1): si = dp[v] else: si = M - dp[u] sum_same += si * (si - 1) // 2 cur = total - sum_same if cur > max_cnt: max_cnt = cur res_node = u print(res_node) if __name__ == "__main__": main()
代码时间复杂度为O(N),可以处理规模较大的树。
内容的提问来源于stack exchange,提问作者Lam Cherry
相关产品推荐
相关产品推荐

