【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