Section 2.3-nocows

题目大意

    一群奶牛彼此之间的族系关系构成了一棵二叉树,构成的过程为:每只奶牛要么不生,要么生两只奶牛。将这样一棵族系关系二叉树称作这群奶牛的族谱,显然一群奶牛可能有很多种族谱。
    这些族谱二叉树有N个节点,且具有如下性质:
        1、每个节点的度为0或2,对应为节点奶牛孩子的数目。
        2、树的高度等于K,高度是从根到最远的那个叶子所需
    要经过的结点数,叶子是指没有孩子的节点。

    那么一共有多少种族谱?输出可能的家谱树的个数除以9901的余数。

输入格式

    line 1: 两个由空格分开的整数:N K;

输出格式

    line 1:一个整数,表示可能的家谱树的个数除以9901的余数。

样例输入

5 3

样例输出

2

题解

    以样例为例,节点数为5,高度为3,且满足题目条件的二叉树只有以下两种:
     @                                 @
    / \                               / \
   @   @            和               @   @
  / \                                   / \
 @   @                                 @   @
     经过尝试,感觉以这道题的数据量是不可能搜索所有可能情况然后在输出的。所以树的种数应该是满足某种递推关系,然后采用动态规划的办法算出来的。
     那么怎样构成一棵满足要求的树便是关键了。我解题的时候先采用一棵节点为N-1的树和一个节点来构成一棵节点为N的树,然后失败了。。。。后来采用比较大众的思路,一棵树由根节点和它的左右子树构成。如果树的节点数为N,左子树的节点为i,那么右子树节点为N-i-1;同时若数的高度为K,那么左右子树的高度一定不能超过K-1,并且至少有一个为K-1;
     由此递推关系就来了:一棵节点数为N,高度为K的树的总可能数为以下三种情况之和:
     1、左子树深度为K-1 ,右子树深度小于K-1;
     2、右子树深度为K-1,左子树深度小于K-1;
     3、左右子树深度都为K-1;
    (当然左右子树的节点数是从1到N-2变化的)
     在实际实现过程中,我的解法陷入了一个困境,就是当为可能1,2时,深度小于K-1的数的个数需要累加出来,同时还要考虑节点数的可能性。造成代码很冗长,而且会有奇异解。经过一两天的思考,决定参考一下标程。发现标程中非常厉害的引入了一个smalltree数组,将深度小于K-1的树的种数也套在动态规划里递推出来了。
     用table[i][j]表示深度为i、节点数为j的树的个数。用另一个数组smalltrees[i-2][j]记录所有深度小于i-1的树,而不仅仅是深度为i-2的树,则有:
     table[i][j] := smalltrees[i-2][k]*table[i-1][j-1-k];
              // 左子树深度小于i-1,右子树深度为i-1
    table[i][j] := table[i-1][k]*smalltrees[i-2][j-1-k];
              // 左子树深度为i-1,右子树深度小于i-1
    table[i][j] := table[i-1][k]*table[i-1][j-1-k];
              // 左右子树深度都为i-1 

代码

#include<stdio.h>
int N=0,K=0;
int trees[210][210]={0};
int strees[210][210]={0};
int main()
{
    int n,i,k,v,w,c;
    scanf("%d %d",&N,&K);
    trees[1][1]=1;
    trees[2][3]=1;

    for(k=3;k<=K;k++)
        for(n=5;n<=N;n=n+2)
        {

            /*for(v=1;v<=n-2;v=v+2)
                {
                    strees[k-2][v]=strees[k-3][v]+trees[k-2][v];
                    strees[k-2][v]=strees[k-2][v]%9901;
                }*/

        for(v=1;v<=n-2;v=v+2)//这步与上一步等效
        {
            strees[k-2][v]=0;
            for(w=1;w<=k-2;w++)
                {
                    strees[k-2][v]=strees[k-2][v]+trees[w][v];
                    strees[k-2][v]=strees[k-2][v]%9901;
                }
        }
            for(i=1;i<=n-1;i=i+2)
            {
                c=2;
                if(n-1-i==i)
                    c=1;
                trees[k][n]=2*trees[k-1][n-1-i]*strees[k-2][i]+trees[k-1][n-1-i]*trees[k-1][i]+trees[k][n];
                trees[k][n]=trees[k][n]%9901;
            }
        }
    printf("%d\n",trees[K][N]);
    return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值