Codeforces 9D 卡特兰数,二叉树种类

题意:

给定 n , h n,h n,h,求用 n n n的结点组成的高度 ≥ h \geq h h的二叉树有多少种

方法:

TIPS:二叉树计数左右儿子不等价,(x,y)和(y,x)是两种可能(x!=y)

首先先从任意二叉树计数开始,是一个经典的卡特兰数问题,这里利用 d p dp dp来求卡特兰数,设 f [ i ] f[i] f[i] i i i个结点组成的不同的二叉树种数,那么要求 d p [ i ] dp[i] dp[i],先放置根节点,然后就是左右子树有多少种的问题,枚举左子树的大小 j j j,有

f [ i ] = ∑ j = 0 i − 1 f [ j ] ∗ f [ i − j − 1 ] f[i]=\sum_{j=0}^{i-1}f[j]*f[i-j-1] f[i]=j=0i1f[j]f[ij1]

那么在这里有一个高度限制,于是添加一维限制高度,设 d p [ i ] [ j ] dp[i][j] dp[i][j] i i i个结点组成的深度为 j j j的二叉树有多少种,要求 d p [ i ] [ j ] dp[i][j] dp[i][j],同样先枚举左子树大小 k k k,此时左子树的大小 h l h_{l} hl可能 < j − 1 <j-1 <j1 = = j − 1 ==j-1 ==j1

h l < j − 1 h_{l}<j-1 hl<j1,要是最后深度为 j j j,那么右子树深度要是 j − 1 j-1 j1,于是

d p [ i ] [ j ] = ∑ k = 0 i − 1 ∑ l = 0 j − 2 d p [ k ] [ l ] ∗ d p [ 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=0i1l=0j2dp[k][l]dp[ik1][j1]

h l = = j − 1 h_{l}==j-1 hl==j1,那么右子树深度不超过 j − 1 j-1 j1即可,于是

d p [ i ] [ j ] = ∑ k = j − 1 i − 1 ∑ l = 0 j − 1 d p [ k ] [ j − 1 ] ∗ d p [ 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=j1i1l=0j1dp[k][j1]dp[ik1][l]

实际上,这两种就是左右子树交换的重复计数,但是不建议并且不能计算一边的 a n s ans ans × 2 \times 2 ×2,当左右子树规模与深度完全一样是,这确实是一种,其他的是两种,并不是所有的计数都要 × 2 \times 2 ×2的,而把规模完全一样的计数分离开又添加了难度和空间,不如直接分类讨论。一开始样例一算了个2,问题应该是在开始以为左右儿子等价并且少算了左右规模和深度一样的情况

这样的计数方法更偏向是组合数学,而不是树形 d p dp dp,这是基于卡特兰数的多限制的一种 d p dp dp

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值