Codeforces

https://codeforces.com/problemset/problem/9/D

一开始居然还想直接找公式的,想了想还是放弃了。原来这种结构是要动态规划。

状态是知道怎么设了,$t_{nh}$ 表示节点数为n个,树高为h的BST的个数

为什么要这么设状态呢?是考虑到题目关心BST的高度,而且BST具有递归性。

但是这个关键就是要加上n个节点的标记,因为不同节点数量明显可以构造出的高为h的树不同,而且也会影响BST另一侧的构造。

所以说设对状态就做对了一半。

 思路就是按高为h的BST是怎么由高为 $h-1$ 的BST加上根节点转移过来的,重点是向上转移而不是向下转移。

首先你要分类讨论,这个根节点的数字是 $m$ ,那么左边会有 $1 hicksim m-1$ 共 $m-1$ 个节点,右边则有 $n-m$ 个节点,然后分

1.左子树的高是 $h-1$ ,右子树的高是 $0 hicksim h-1$ (假如可以放得下的话)。

2.右子树的高是 $h-1$ ,左子树的高是 $0 hicksim h-2$ (注意是h-2,否则和上面重复)

左右子树的构造是独立的,所以相乘。两种情况的分类的,所以相加。

最后遍历一遍 $m$ 得到所有 $t_{nh}$ 的和。

最后在自己做的时候,犯了几个小问题,首先就是 $m$ 的意思,定义中是指 $root$ 的值,写的时候变成的左子树的节点数,所以挂了。其次是 $t_{11}$ 虽然是平凡情况但是我用 $+=$ 运算的话会出毛病。第三是题目要求的是高度不小于 $h$ 的 $n$ 节点BST的总数,那么应该是 $sum(n,maxh=n)-sum(n,maxh=h-1)$ 。

#include<bits/stdc++.h>
using namespace std;
#define ll long long

unsigned ll t[36][36]={};

unsigned ll sum(int n,int maxh){
    unsigned ll res=0;
    for(int i=0;i<=maxh;i++){
        res+=t[n][i];
    }
    //printf("sum(t[n=%d][maxh=%d])=%llu
",n,maxh,res);
    return res;
}

int n,h;
void solve(){
    t[0][0]=1;
    //空树也是1种情况,因为要算乘法
    for(int i=1;i<=n;i++){
        t[i][0]=0;
        t[0][i]=0;
        //这两种树不可能构造
    }

    for(int ni=1;ni<=n;ni++){
        for(int hi=1;hi<=n;hi++){
            if(ni<hi)
                continue;
            for(int m=1;m<=ni;m++){
                t[ni][hi]+=t[m-1][hi-1]*sum(ni-m,hi-1);

                //printf("t[%d][%d]+=%llu*%llu
",ni,hi,t[m-1][hi-1],sum(ni-m,hi-1));
                t[ni][hi]+=sum(m-1,hi-2)*t[ni-m][hi-1];
            }
            //printf("t[%d][%d]=%llu
",ni,hi,t[ni][hi]);
        }
    }
}

int main(){
    scanf("%d%d",&n,&h);
    solve();
    unsigned ll ans=0;
    ans=sum(n,n)-sum(n,h-1);
    printf("%llu
",ans);
}
原文地址:https://www.cnblogs.com/Yinku/p/10287466.html