题目描述
难度分:1700
输入T(≤104)表示T组数据。所有数据的a+b+c之和≤3×105。
每组数据输入a,b,c(0≤a,b,c≤105且1≤a+b+c)。
有一棵二叉树,其中a个节点有两个儿子,b个节点有一个儿子,c个节点没有儿子。输出这棵二叉树的最小高度。
注意这里的高度是根到最远叶节点的路径上的边的个数。特别地,只有一个节点的二叉树的高度为 0。
输入样例
10
2 1 3
0 0 1
0 1 1
1 0 2
1 1 3
3 1 4
8 17 9
24 36 48
1 0 0
0 3 1
输出样例
2
0
1
1
-1
3
6
-1
-1
3
算法
贪心
这个题看着就感觉有O(1)的数学解法,但是思考起来不是特别容易想清楚,所以赛场上比较保守的做法还是用小根堆进行贪心。首先做个过滤,当c≠a+1的时候无解,输出−1,其余情况都有解。
贪心的策略就是尽量往深度最小的位置追加节点,首先将深度0加入到一个小根堆中,然后按以下步骤进行贪心:
- 考虑度为2的节点,一共有a个这样的节点,考虑a次。每考虑一个节点的时候就弹出堆顶的深度d,此时这个度为2的节点会产生两个深度为d+1的节点,将两个d+1加入到小根堆中。
- 考虑度为1的节点,一共有b个这样的节点,考虑b次。每考虑一个节点的时候就弹出堆顶的深度d,此时这个度为1的节点会产生一个深度为d+1的节点,将d+1加入到小根堆中。
由于肯定有解,我们不需要考虑c的约束,最后看看小根堆中的深度最大值,这个最大值就是我们要求的最小树高。
复杂度分析
时间复杂度
过一遍度为2的节点和度为1的节点需要遍历a+b次,如果n=a+b+c,在极限情况下b=0,整棵树是一个满二叉树,a和c的量级都为O(n2)规模,即O(n)。而每次都需要往小根堆插中入元素,时间复杂度为O(log2n)。因此,整个算法的时间复杂度为O(nlog2n)。
空间复杂度
空间复杂度由优先队列的消耗决定,在极限情况下b=0,会往小根堆中加入a个元素,是O(n)规模的元素数量。因此,算法的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
int T, a, b, c;
int main() {
scanf("%d", &T);
while(T--) {
// a个节点有两个孩子,b个节点有一个孩子,c个叶子节点
scanf("%d%d%d", &a, &b, &c);
if(c != a + 1) {
puts("-1");
continue;
}
priority_queue<int, vector<int>, greater<int>> heap;
heap.push(0);
for(int i = 1; i <= a; i++) {
int d = heap.top();
heap.pop();
heap.push(d + 1), heap.push(d + 1);
}
for(int i = 1; i <= b; i++) {
int d = heap.top();
heap.pop();
heap.push(d + 1);
}
int ans = 0;
while(!heap.empty()) {
ans = max(ans, heap.top());
heap.pop();
}
printf("%d\n", ans);
}
return 0;
}