Construct the Binary Tree
题意
给你n个结点,问是否可以构造成一个各结点深度之和为d的二叉树,如果可以输出YES和每个结点的父结点编号,如果不可以输出NO。根结点的编号固定为1
分析
这题很明显是个构造题,一向构造题弱项的我,写了3个小时也没有写出来,不过在参考了别人的博客之后,知道了怎么样能够更好的实现。这题的思路,如果让用手模拟的话很简单,但是要用代码来描述就比较困难了,所以也可以算是模拟算法题目。
我参考了这篇博客:传送门
我们知道n个结点,构成深度之和最小的是完全二叉树,构成深度最大的是一条链。如果给定的d小于完全二叉树,或大于一条链的深度之和,则无法构成一个树,否则就一定可以。
我们将完全二叉树一步一步的向链进行转化,可以发现,只要不是链,我们总可以找到一个叶结点(除链上的最下面一个叶结点)。所以我们就是循环找到一个叶结点一步一步往下移动,也就是深度之和会逐步+1,直到变成d。
具体的做法就是上面图片所示,其中红色部分是准备转换成链的部分。每次是找非链上编号最大结点开始往下移动,一直移动到最底端,使得链长+1,然后再找非链上编号最大的下一个结点开始往下移动。
AC 代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 5e3+10;
int dp[maxn],fa[maxn],list[maxn],in_list[maxn];
//保存结点深度,结点的父结点,某个深度链上结点是谁,某个结点是否在链上
int T,n,d;
void fun(){
memset(dp,0,sizeof dp);
memset(fa,0,sizeof fa);
memset(list,0,sizeof list);
memset(in_list,0,sizeof in_list);
int sum = 0,mxdp = 0;
for(int i = 2;i<=n;i++){
fa[i] = i/2;
dp[i] = dp[fa[i]]+1;
sum += dp[i];
mxdp = max(mxdp,dp[i]);
}
if(d < sum || d > n*(n-1)/2) { //不满足的情况
puts("NO");
}else{
puts("YES");
int cur = n;
while(cur){ //记录每一深度链上结点编号
list[dp[cur]] = cur;
in_list[cur] = 1;
cur = fa[cur];
}
for(int i = n;i>=1;i--){
if(in_list[i]) continue; //链上结点不可移动
while(dp[i] <= mxdp && sum < d){
fa[i] = list[dp[i]];
dp[i]+=1;
sum +=1;
if(dp[i] >mxdp){ //已经把新来的结点加在了最底端了。
mxdp = dp[i];
list[dp[i]] = i;
in_list[i] = 1;
break;
}
}
}
for(int i = 2;i<=n;i++) printf("%d ",fa[i]);
puts("");
}
}
int main(){
cin>>T;
while(T--){
scanf("%d %d",&n,&d);
fun();
}
return 0;
} 
京公网安备 11010502036488号