• USACO 2.3 Cow Pedigrees 【DP+前缀和优化】


    题目:

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

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

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

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

    分析:

    这是一道动态规划题目,问什么设什么,于是:

    我们设f[i][j]表示结构为i层,j个结点的树的个数。
    若想要构造它,就必须用它的两个子树去拼它。因为它是i层,所以它必须有至少一个子树的深
    度为i-1才能“撑起”它,所以我们分了3种情况:

    左子树深度为i-1,右子树深度小于i-1;
    左子树深度小于i-1,右子树深度为i-1;
    左、右子树深度都为i-1;

    事实上,当我们在构造一棵深度为i的树时,我们只关心使用的子树深度是否为i-1或更小。
    因此,我们用一个small数组表示深度小于i-1且有j个节点的树的个数。实际操作时我们会
    发现,small实际上是一个前缀和数组。

    下面是代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mod 9901
    using namespace std;
    int f[102][202];
    int small[102][202];
    int num,lev;
    void DP()
    {
     f[1][1]=1;
     for(int i=2;i<=lev;i++)
     {
      for(int j=1;j<=num;j+=2)
      {
       for(int k=1;k<=j-k-1;k+=2)
       { 
        int c=0; 
        if(k==j-k-1)
         c=1;
        else
         c=2;
        f[i][j]+=c*(f[i-1][k]*f[i-1][j-k-1]);
        f[i][j]+=c*(f[i-1][k]*small[i-2][j-k-1]);
        f[i][j]+=c*(f[i-1][j-k-1]*small[i-2][k]);
        f[i][j]%=mod;
       }
       for(int p=0;p<=num;p++)
       {
        small[i-1][p]=f[i-1][p]+small[i-2][p];
        small[i-1][p]%=mod;
       }
      }
       
     }
      
    }
    int main()
    {
     cin>>num>>lev;
     DP();//how many dotts and how many levels
     cout<<f[lev][num]<<endl; 
     return 0;
    }
    View Code

    或者不要把更新small放到第二层循环中,在求出一个f[i][j] 后,更新small,要注意的是,此时要用“+=”

    我推荐这个版本,它的时间复杂度低一些。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mod 9901
    using namespace std;
    int f[102][202];
    int small[102][202];
    int num,lev;
    void DP()
    {
     f[1][1]=1;
     for(int i=2;i<=lev;i++)
     {
      for(int j=1;j<=num;j+=2)
      {
       for(int k=1;k<=j-k-1;k+=2)
       { 
        int c=0; 
        if(k==j-k-1)
         c=1;
        else
         c=2;
        f[i][j]+=c*(f[i-1][k]*f[i-1][j-k-1]);
        f[i][j]+=c*(f[i-1][k]*small[i-2][j-k-1]);
        f[i][j]+=c*(f[i-1][j-k-1]*small[i-2][k]);
        f[i][j]%=mod;
       }
      }
      for(int p=0;p<=num;p++)
      {
       small[i-1][p]+=f[i-1][p]+small[i-2][p];
       small[i-1][p]%=mod;
      }
     }
      
    }
    int main()
    {
     cin>>num>>lev;
     DP();//how many dotts and how many levels
     cout<<f[lev][num]<<endl; 
     return 0;
    }
    View Code
  • 相关阅读:
    Twitter OA prepare: Rational Sum
    Java: Best Way to read a file
    Summary: gcd最大公约数、lcm最小公倍数算法
    Twitter OA prepare: Flipping a bit
    Twitter OA prepare: Equilibrium index of an array
    echo -e 参数
    openwrt 添加luci选项
    基于TLS的EAP 认证方法
    linux命令 dirname
    freeradius 错误: error:140890C7:SSL routines:ssl3_get_client_certificate:peer did not return a certificate
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/7206180.html
Copyright © 2020-2023  润新知