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); }