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

 

posted @ 2017-07-19 15:49  Captain_fcj  阅读(176)  评论(0编辑  收藏  举报