题意:
给定n,hn,hn,h,求用nnn的结点组成的高度≥h\geq h≥h的二叉树有多少种
方法:
TIPS:二叉树计数左右儿子不等价,(x,y)和(y,x)是两种可能(x!=y)
首先先从任意二叉树计数开始,是一个经典的卡特兰数问题,这里利用dpdpdp来求卡特兰数,设f[i]f[i]f[i]为iii个结点组成的不同的二叉树种数,那么要求dp[i]dp[i]dp[i],先放置根节点,然后就是左右子树有多少种的问题,枚举左子树的大小jjj,有
f[i]=∑j=0i−1f[j]∗f[i−j−1] f[i]=\sum_{j=0}^{i-1}f[j]*f[i-j-1] f[i]=j=0∑i−1f[j]∗f[i−j−1]
那么在这里有一个高度限制,于是添加一维限制高度,设dp[i][j]dp[i][j]dp[i][j]为iii个结点组成的深度为jjj的二叉树有多少种,要求dp[i][j]dp[i][j]dp[i][j],同样先枚举左子树大小kkk,此时左子树的大小hlh_{l}hl可能<j−1<j-1<j−1或==j−1==j-1==j−1
当hl<j−1h_{l}<j-1hl<j−1,要是最后深度为jjj,那么右子树深度要是j−1j-1j−1,于是
dp[i][j]=∑k=0i−1∑l=0j−2dp[k][l]∗dp[i−k−1][j−1] dp[i][j]=\sum_{k=0}^{i-1} \sum_{l=0}^{j-2}dp[k][l]*dp[i-k-1][j-1] dp[i][j]=k=0∑i−1l=0∑j−2dp[k][l]∗dp[i−k−1][j−1]
当hl==j−1h_{l}==j-1hl==j−1,那么右子树深度不超过j−1j-1j−1即可,于是
dp[i][j]=∑k=j−1i−1∑l=0j−1dp[k][j−1]∗dp[i−k−1][l] dp[i][j]=\sum_{k=j-1}^{i-1}\sum_{l=0}^{j-1} dp[k][j-1]*dp[i-k-1][l] dp[i][j]=k=j−1∑i−1l=0∑j−1dp[k][j−1]∗dp[i−k−1][l]
实际上,这两种就是左右子树交换的重复计数,但是不建议并且不能计算一边的ansansans再×2\times 2×2,当左右子树规模与深度完全一样是,这确实是一种,其他的是两种,并不是所有的计数都要×2\times 2×2的,而把规模完全一样的计数分离开又添加了难度和空间,不如直接分类讨论。一开始样例一算了个2,问题应该是在开始以为左右儿子等价并且少算了左右规模和深度一样的情况
这样的计数方法更偏向是组合数学,而不是树形dpdpdp,这是基于卡特兰数的多限制的一种dpdpdp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,h;
ll ans,dp[50][50];
int main()
{
cin>>n>>h;
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
for(int k=j-1;k<=i-1;k++)
{
for(int l=0;l<=min(i-k-1,j-1);l++)
{
dp[i][j]+=dp[k][j-1]*dp[i-k-1][l];
}
}
for(int k=0;k<=i-1;k++)
{
for(int l=0;l<=j-2;l++)
{
dp[i][j]+=dp[k][l]*dp[i-k-1][j-1];
}
}
}
}
ll ans=0;
for(int i=h;i<=n;i++) ans+=dp[n][i];
cout<<ans;
return 0;
}
该博客探讨了如何使用动态规划解决一个组合数学问题,即在给定节点数n和最小高度h的情况下,计算所有不同高度大于等于h的二叉树数量。博主通过分类讨论避免了左右子树规模和深度相同的重复计数,详细阐述了多限制下的卡特兰数动态规划实现,展示了相关代码并解释了错误的计数方法。
1585

被折叠的 条评论
为什么被折叠?



