• USACO / Cow Pedigrees(DP)


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

    描述

    每一个节点的度是0或2。度是这个节点的孩子的数目。


    树的高度等于K(1 < K < 100)。高度是从根到最远的那个叶子所需要经过的结点数; 叶子是指没有孩子的节点。

     

    有多少不同的家谱结构? 如果一个家谱的树结构不同于另一个的, 那么这两个家谱就是不同的。输出可能的家谱树的个数除以9901的余数。

    格式

    PROGRAM NAME: nocows


    INPUT FORMAT (file nocows.in)


    第1行: 两个空格分开的整数, N和K。

     

    OUTPUT FORMAT (file nocows.out)


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

    SAMPLE INPUT

    5 3
    

    SAMPLE OUTPUT

    2
    

    OUTPUT DETAILS

    有5个节点,高为3的两个不同的家谱:

               @                              @
              / \                            / \
             @   @        和                @   @
            / \                                / \
           @   @                              @   @
    分析:
    这道题因为对树不太熟悉,自己没有想出来,,,看的官方的解题报告:

    这是一个DP问题。我们所关心的树的性质是深度和节点数,所以我们可以做这样一张表:table[i][j]表示深度为i、节点数为j的树的个数。根据给定的约束条件,j必须为奇数。你如何构造一棵树呢?当然是由更小的树来构造了。一棵深度为i、节点数为j的树可以由两个子树以及一个根结点构造而成。当i、j已经选定时,我们选择左子树的节点数k。这样我们也就知道了右子树的节点数,即j-k-1。至于深度,至少要有一棵子树的深度为i-1才能使构造出的新树深度为i。有三种可能的情况:左子树深度为i-1 ,右子树深度小于i-1;右子树深度为i-1,左子树深度小于i-1;左右子树深度都为i-1。事实上,当我们在构造一棵深度为i的树时,我们只关心使用的子树深度是否为i-1或更小。因此,我们使用另一个数组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 
    另外,如果左子树更小,我们可以对它进行两次计数,因为可以通过交换左右子树来得到不同的树。总运行时间为O(K*N^2),且有不错的常数因子。
    代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cassert>
    #define MOD 9901
    using namespace std;
     
    int table[101][202],N,K,c;
    int smalltrees[101][202];
     
    FILE *fin=fopen("nocows.in","r");
    FILE *fout=fopen("nocows.out","w");
     
    int main() {
        fscanf (fin,"%d %d",&N,&K);
        table[1][1]=1;
        for (int i=2;i<=K;i++) {
            for (int j=1;j<=N;j+=2)
                for (int k=1;k<=j-1-k;k+=2) {
                    if (k!=j-1-k) c=2; else c=1;  //判断树的结构是否对称  
                    table[i][j]+=c*(
                            smalltrees[i-2][k]*table[i-1][j-1-k]  // 左子树深度小于i-1
                            +table[i-1][k]*smalltrees[i-2][j-1-k]  // 右子树深度小于i-1
                            +table[i-1][k]*table[i-1][j-1-k]);// 都为i-1
                    table[i][j]%=MOD;
                }
            for (int k=0;k<=N;k++) { 
            // 确保接下来第i次迭代中的smalltrees[i-2][j]包含了深度小于i-1且节点数为j的树的个数
                smalltrees[i-1][k]+=table[i-1][k]+smalltrees[i-2][k]; 
                smalltrees[i-1][k]%=MOD; 
            }
        }
     
        fprintf (fout,"%d\n",table[K][N]);
        return 0;
    }
     
    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    eclipse上传下载github
    GitHub整合idea 上传和下载
    Git安装与配置
    第八天springboot整合redis
    第七天.spring boot 整合mybatis并使用Junit进行测试
    ssm的各种配置资源
    Git相关软件下载
    Eclipse上传项目到GitHub
    SpringBoot使用spring data jpa及在页面yaml中显示
    spring boot 整合mybatis及使用Junit进行测试
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/2598501.html
Copyright © 2020-2023  润新知