满二叉树等长路径
满二叉树等长路径
给定一个深度为 $n$ 的满二叉树,其 $2^{n+1}−1$ 个顶点的编号为 $1 \sim 2^{n+1}−1$。
树的根节点为 $1$ 号节点,除根节点外,第 $i$ 号节点的父节点为第 $\left\lfloor \frac{i}{2} \right\rfloor$ 号节点。
例如,当 $n=3$ 时,二叉树如下所示:
树中每条边的长度已知,由此可以得到根节点到 $2^{n}$ 个叶节点的距离。
为了使得根节点到每个叶节点的距离都相等,我们可以进行任意次增边操作。
每次操作可以选择任意一条边,将其增加任意正整数长度。
我们希望在达成目的的同时,所有边的总增加长度尽可能小。
请你计算并输出总增加长度的最小可能值。
输入格式
第一行包含整数 $n$。
第二行包含 $2^{n+1}−2$ 个整数 $a_{2},a_{3},…,a_{2^{n+1}−1}$,其中 $a_{i}$ 表示第 $i$ 号节点与第 $\left\lfloor \frac{i}{2} \right\rfloor$ 号节点之间的边的长度。
输出格式
一个整数,表示总增加长度的最小可能值。
数据范围
前三个测试点满足 $1 \leq n \leq 2$。
所有测试点满足 $1 \leq n \leq 10,1 \leq a_{i} \leq 100$。
输入样例:
2 1 2 3 4 5 6
输出样例:
5
解题思路
这题在做的时候猜对了做法,贪心题,难得。
假设根节点到叶子结点的距离都相同,对于上面的左子树,每一个叶子结点到左儿子的距离是相同的。如果不相同,意味着存在两个叶子结点到左儿子的距离不同,即这两个叶子结点到根节点的距离不相等,就与根节点到叶子结点的距离都相同矛盾了。右子树同理。
现在假设左子树的每个叶子结点到根节点的距离为$x$,右子树的每个叶子结点到根节点的距离为$y$。现在我们希望根节点到左右子树的叶子结点的距离都相同,假设最终的距离为$d$,由于只能够增加距离,因此有$d \geq max \left\{ {x, y} \right\}$。那么左子树的每一个叶子结点到根节点的距离就要增加$d - x$,右子树的每一个叶子结点到根节点的距离就要增加$d - y$。
对于左子树,我们看一下$d - x$增加在哪个地方。如果增加在根节点到左儿子的这条边上,那么只用增加一次。如果在第二层增加,那么就要增加$2 \times \left( {d - x} \right)$次(左儿子的两条边各一次),如果在第三层增加,就要增加$2^{2} \times \left( {d - x} \right)$次,以此类推,所以为了增加的数量尽可能少,我们应该在根节点到左儿子的这条边上增加。右子树同理。
所以增加的总代价为$d - x + d - y = 2 \cdot d - \left( {x + y} \right)$,要让代价最小,那么$d$就应该最小,即$d = max \left\{ {x, y} \right\}$,因此最小代价就为$\left| {x - y} \right|$。
可以发现,$x$是每个左子树的叶子节点到根节点的最小距离,$y$是每个右子树的叶子节点到根节点的最小距离。对于$\left| {x - y} \right|$,如果$x$稍微增大一些,那么这个绝对值就会减少。假设增加$c$,可这意味着从第二层开始,至少要增加$2 \times c$,这样代价反而会增加。虽然$x$增加$c$后会对当前这层的代价减少,但总的代价会增加,因此每次都应该取到根节点的最小距离。
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 using namespace std; 5 6 const int N = (1 << 11) + 10; 7 8 int n, ans; 9 int a[N]; 10 11 // dfs返回叶子节点到u的最小距离 12 int dfs(int u) { 13 if (u << 1 > (1 << n + 1) - 1) return 0; // 不存在左儿子,什么是叶子节点 14 15 int l = dfs(u << 1) + a[u << 1]; // 求左子树的叶子结点到u的距离 16 int r = dfs(u << 1 | 1) + a[u << 1 | 1];// 求右子树的叶子结点到u的距离 17 ans += abs(l - r); 18 19 return max(l, r); // l和r的最大距离就是u到叶子节点的最小距离 20 } 21 22 int main() { 23 scanf("%d", &n); 24 25 int m = (1 << n + 1) - 1; 26 for (int i = 2; i <= m; i++) { 27 scanf("%d", a + i); 28 } 29 30 dfs(1); 31 printf("%d", ans); 32 33 return 0; 34 }
参考资料
AcWing 4312. 出现次数(AcWing杯 - 周赛):https://www.acwing.com/video/3729/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16000030.html