1 条题解
-
0
问题重述
我们有一棵 个节点的二叉树,每个节点 初始有 道题目。需要按某种顺序断开所有边,每次断开边 时, 和 会交换它们当前的所有题目,交换的题目数量等于两者当前题目数之和。目标是 minimize 总交换题目数。
关键观察
1. 交换的累积效应
设节点 在断开与父节点的边之前,其子树内的总交换次数为 ,子树内的总题目数为 。
当断开 与父节点的边时:
- 交换的题目数 = 的当前题目数 + 父节点的当前题目数
- 但 的当前题目数 = + 从子节点交换来的题目
2. 子树贡献分析
对于节点 ,假设它有左右子节点 和 。
断开边 和 的顺序会影响总代价:
-
先断左后断右:
- 断 :代价 =
- 断 :代价 =
-
先断右后断左:对称情况
实际上,经过推导可以发现:对于节点 ,断开左右子树的顺序不影响该节点贡献的总代价。
核心解法
1. 树形 DP 状态设计
设:
- = 以 为根的子树中的初始题目总数
- = 断开以 为根的子树中所有边的最小总代价
2. 状态转移
对于节点 :
-
如果没有子节点:,
-
如果只有一个子节点 :
dp[u] = dp[v] + size[v] + d_u size[u] = size[v] + d_u解释:断开 时, 有 题, 有 题(因为 的子树已断开),代价为 ,再加上 子树的代价
-
如果有两个子节点 和 :
dp[u] = dp[l] + dp[r] + size[l] + size[r] + d_u size[u] = size[l] + size[r] + d_u解释:无论先断左还是先断右,总代价都是 而断开 时, 有 题, 有 题,代价为 断开 时, 有 题, 有 题,代价为 两者相同,所以顺序不影响。
3. 最终答案
整棵树断开的总代价就是 ,其中 。
算法步骤
- 建树:根据输入的 构建二叉树
- DFS 后序遍历:计算每个节点的 和
- 输出:
样例验证
以样例为例:
n = 3 d = [2, 1, 3] c = [1, 1] # 表示节点2和节点3的父节点都是1树结构:节点1有两个子节点2和3
计算过程:
- 节点2:,
- 节点3:,
- 节点1:$dp[1] = dp[2] + dp[3] + size[2] + size[3] + d_1 = 0 + 0 + 1 + 3 + 2 = 6$
但样例答案是7,说明我们的推导还需要修正。
修正分析
重新分析两个子节点的情况:
情况1:先断左后断右
- 断 : 有 , 有 ,代价 =
- 断 : 有 , 有 ,代价 =
- 总代价 = $dp[l] + dp[r] + (d_u + size[l]) + (d_u + size[l] + size[r])$
情况2:先断右后断左
- 断 : 有 , 有 ,代价 =
- 断 : 有 , 有 ,代价 =
- 总代价 = $dp[l] + dp[r] + (d_u + size[r]) + (d_u + size[r] + size[l])$
选择最优顺序:
- 如果 ,先断左再断右更优
- 如果 ,先断右再断左更优
因此正确的转移方程:
dp[u] = dp[l] + dp[r] + min( size[l] + (2*d_u + size[l] + size[r]), # 先左后右 size[r] + (2*d_u + size[l] + size[r]) # 先右后左 ) = dp[l] + dp[r] + 2*d_u + size[l] + size[r] + min(size[l], size[r])
最终算法
对于节点 :
- 叶子节点:,
- 一个子节点 :,
- 两个子节点 :$dp[u] = dp[l] + dp[r] + 2*d_u + size[l] + size[r] + min(size[l], size[r])$,
复杂度分析
- 时间复杂度:,每个节点处理一次
- 空间复杂度:,存储树结构和DP状态
总结
这道题的核心在于:
- 理解交换的累积效应:节点题目数随交换过程变化
- 分析断开顺序的影响:对于二叉树节点,需要选择先断开哪个子树的边
- 贪心策略:优先断开子树总题目数较小的边
- 树形DP:自底向上计算最小代价
通过仔细分析交换过程和断开顺序的影响,我们得到了一个简洁高效的线性算法。
- 1
信息
- ID
- 4438
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者