[USACO2.3.2]Cow Pedigrees奶牛家谱

题目

题目描述
农民约翰准备购买一群新奶牛。 在这个新的奶牛群中, 每一个母亲奶牛都生两小奶牛。这些奶牛间的关系可以用二叉树来表示。这些二叉树总共有 N ( 3 ≤ N ≤ 200 ) N(3\le N\le 200) N(3N200) 个节点。这些二叉树有如下性质:

  • 每一个节点的度是 0 0 0 2 2 2 。度是这个节点的孩子的数目。
  • 树的高度等于 K ( 1 < K < 100 ) K(1 < K < 100) K(1<K<100) 。高度是从根到任何叶子的最长的路径上的节点的数目;叶子是指没有孩子的节点。

有多少不同的家谱结构?如果一个家谱的树结构不同于另一个的,那么这两个家谱就是不同的。

输出可能的家谱树的个数除以 9901 9901 9901 的余数。

输入格式
一行两个正整数 n , k n,k n,k ,含义见题面。

输出格式:
一行一个非负整数,表示不同的家谱结构的数目除以 9901 9901 9901 的余数。

数据范围与约定
对于 100 % 100\% 100%的数据, n ≤ 200 , k ≤ 100 n\le 200,k\le 100 n200,k100

思路

f ( i , j ) f(i,j) f(i,j) 表示有 i i i 个节点,深度 不超过 j j j ,能够形成的家谱树的数量。

那么枚举左子树的节点个数 x x x (右字数节点数自然为 i − x − 1 i-x-1 ix1 ),根据乘法原理,有

f ( i , j ) = ∑ x = 0 i − 1 f ( x , j − 1 ) f ( i − x − 1 , j − 1 ) f(i,j)=\sum_{x=0}^{i-1}f(x,j-1)f(i-x-1,j-1) f(i,j)=x=0i1f(x,j1)f(ix1,j1)

这是 O ( n 2 k ) \mathcal O(n^2k) O(n2k) 的,可以接受。但我们要求的不是 f ( n , k ) f(n,k) f(n,k)

因为 f ( n , k ) f(n,k) f(n,k) 包含了深度为 1 1 1 k k k 的所有可能。我们想知道的是深度 恰好 k k k 的数量。那么多了个什么东西呢?深度为 1 1 1 k − 1 k-1 k1 的数量。不正是 f ( n , k − 1 ) f(n,k-1) f(n,k1) 吗?作差即可。

还有一些小细节,稍微提一下:

  • 每一种合法的家谱都是奇数个节点。
  • f ( n , k ) f(n,k) f(n,k) 在模意义下可能小于 f ( n , k − 1 ) f(n,k-1) f(n,k1) ,输出时应当处理。

代码

#include 
const int M = 9901;
int dp[202][102];
int main(){
    int n, k;
    scanf("%d %d",&n,&k);
    for(int j=1; j<=k; ++j){
        dp[1][j] = 1;
        for(int i=3; i<=n; i+=2)
            for(int l=1; l<i-1; l+=2)
                dp[i][j]=(dp[i][j]+dp[l][j-1]*dp[i-l-1][j-1])%M;
    }
    printf("%d\n",(dp[n][k]-dp[n][k-1]+M)%M);
    return 0;
}

你可能感兴趣的:(C++,动态规划)