您的位置:首页 > 其它

USACO / Cow Pedigrees(DP)

2012-07-19 01:15 337 查看

农民约翰准备购买一群新奶牛。 在这个新的奶牛群中, 每一个母亲奶牛都生两个小奶牛。这些奶牛间的关系可以用二叉树来表示。这些二叉树总共有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]
);
return 0;
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: