USACO Cow Pedigrees

1、这是一道树形DP的题目,刚开始我的状态转移方程是dp[i][j]=(dp[i][j]+dp[i+1][k]*dp[i+1][j-1-k])%MOD,但这样算出来的个数偏多,原因就是我多算了“深度不够的组合”,我的状态的第一维意思是以第i层结点为根结点,按理说,两个子状态应该至少其中一个会“到底”,但是我的状态并没有办法表示出来这层意思。所以第一维的含义应该改成“树的深度”,而不是“根结点所在的层数”。

/*
ID:mrxy564
PROG:nocows
LANG:C++
*/
#include<cstdio>
#include<cstring>
using namespace std;
const int MOD=9901;
int dp[110][210];
int main(){
 freopen("nocows.in","r",stdin);
 freopen("nocows.out","w",stdout);
    int N,K;
 scanf("%d%d",&N,&K);
 memset(dp,0,sizeof(dp));
 for(int i=1;i<=K;i++)
     dp[i][1]=1;
    for(int i=2;i<=K;i++)
    for(int j=0;j<=N;j++)
     if(dp[i][j]==0)
       for(int k=0;k<=N;k++)
      if(j-1>=k)
                     dp[i][j]=(dp[i][j]+dp[i-1][k]*dp[i-1][j-1-k])%MOD;
    printf("%d\n",(dp[K][N]-dp[K-1][N]+MOD)%MOD);
 return 0;
}

官方题解:

This is a DP problem. The properties of a tree that we are interested in are depth and number of nodes, so we'll make a table: table[i][j] contains the number of trees with depth i and number of nodes j. Given the constraints of the task, j must be odd. How do you construct a tree? From smaller trees, of course. A tree of depth i and j nodes will be constructed from two smaller trees and one more node.

With i and j already chosen, we chose k, which is the number of nodes in the left subtree. Then the number of nodes in the right subtree is known, j-k-1. For depth, at least one subtree has to have depth i-1 so that the new made tree would have depth i. There are three possibilities: the left subtree can have depth i-1 and the depth of the right subtree can be smaller, the right subtree can have depth i-1 and the depth of the left subtree can be smaller, or they can both have depth i-1.

The truth is that once we are constructing trees of depth i, we use smaller trees, but we only care if those are of depth i-1 or smaller. So, let another array, smalltrees[i-2][j] contain number of trees of any depth smaller than i-1, not just i-2. Now, knowing all this, we contruct our tree from three possible ways:

table[i][j] += smalltrees[i-2][k]*table[i-1][j-1-k];
                  // left subtree smaller than i-1, right is i-1
table[i][j] += table[i-1][k]*smalltrees[i-2][j-1-k];
                  // left subtree is i-1, right smaller
table[i][j] += table[i-1][k]*table[i-1][j-1-k];
                  // both i-1 
 

In addition, if the number of nodes in the left subtree is smaller then the number of nodes in the left subtree, we can count the tree twice, as different tree can be constructed by swapping left and right subtree.

Total running time is O(K*N^2),with very favorable constant factor.

#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]  // left subtree smaller than i-1
                        +table[i-1][k]*smalltrees[i-2][j-1-k]  // right smaller
                        +table[i-1][k]*table[i-1][j-1-k]);// both i-1
                table[i][j]%=MOD;
            }
        for (int k=0;k<=N;k++) {          // we ensure that smalltrees[i-2][j] in
the next i
            smalltrees[i-1][k]+=table[i-1][k]+smalltrees[i-2][k]; // iteration
contains the number
            smalltrees[i-1][k]%=MOD;           // of trees smaller than i-1 and with
j nodes
        }
    }
    
    fprintf (fout,"%d\n",table[K][N]);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值