【dp】奶牛家谱 Cow Pedigrees

令人窒息的奶牛题

题目描述

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

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

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

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

输入输出格式

输入格式:

两个空格分开的整数, N和K。

输出格式:

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


 

题目分析

无脑dfs

我们可以显而易见地想到dfs做法。给每一个点赋编号,再接着枚举有无孩子的状态。然后如果再大力剪枝,就可以惊喜地发现到$n=60$之后就TLE到飞起了。

像dp的假dp

这不是一道dp题嘛……考虑一下转移,发现每一种树要么从同一层转移过来、要么从上一层转移过来。那么接着存一存每一种树最下面一层的叶子节点数,再存一存每一种树方案总数,接着就是这些信息的转移……搞来搞去我也搞不清这是什么东西了。

树形dpⅠ

设$f[i][j]$表示$i$个节点构造出深度为$j$的树的方案数。显而易见的是,转移方程为

 

其中,即f[i][k]的前缀和。

在转移的时候,我们只需要枚举右孩子的节点数,再利用乘法原理乘上左孩子深度为$j-1$(除去根)的方案数。之后再将重复的情况减一下就可以了。

这看上去并没有什么问题对吧。

 

 

 

 

 

 

 

 

 

 

 

看到这里大佬您有没有发现这个dp出锅了呢。

 

 

 

是!的!这个人畜无害的dp出锅了!!!

并且截止撰写这篇博客的时候,依然没有找出问题在哪里……

如果大佬您发现了问题,麻烦在评论区留言,谢谢!

 

树形dp Ⅱ

XYZ表示dp方程并不用这么复杂并且表示我的代码画风清奇非常鬼畜

好吧,似乎这份是有点又臭又长的感觉

我们以$f[i][j]$表示$i$个节点,$1..j$深度的方案数的前缀和。那么转移起来就方便很多;并且得益于dp方程的定义,子树节点个数是可以枚举过去的,不存在上面做法方案数乘2的事情,因此也不用管重复的问题了。

 

 1 #include<bits/stdc++.h>
 2 const int MO = 9901;
 3 int f[203][203];
 4 int n,m;
 5 int main()
 6 {
 7     scanf("%d%d",&n,&m);
 8     for (int i=1; i<=m; i++)
 9         f[1][i] = 1;
10     for (int i=1; i<=n; i+=2)
11         for (int j=1; j<=m; j++)
12             for (int k=1; k<i; k+=2)
13                 (f[i][j]+=f[k][j-1]*f[i-k-1][j-1])%=MO;
14     printf("%d\n",(f[n][m]+MO-f[n][m-1])%MO);
15     return 0;
16 }

 

 

这道题真的是挺(wo)有(tai)趣(cai)的(le),这些一步一步的写法倒也是有很多值得深思的地方。

 

(似乎为了这题写的程序数量可以立个记录之类的?)

 

 

END

 

posted @ 2018-03-30 09:09  AntiQuality  阅读(354)  评论(0编辑  收藏  举报