1. 程式人生 > >【DP】奶牛家譜 Cow Pedigrees

【DP】奶牛家譜 Cow Pedigrees

題目描述

農民約翰準備購買一群新奶牛。 在這個新的奶牛群中, 每一個母親奶牛都生兩個小奶牛。這些奶牛間的關係可以用二叉樹來表示。這些二叉樹總共有N個節點(3 <= N < 200)。這些二叉樹有如下性質:

每一個節點的度是0或2。度是這個節點的孩子的數目。

樹的高度等於K(1 < K < 100)。高度是從根到最遠的那個葉子所需要經過的結點數; 葉子是指沒有孩子的節點。

有多少不同的家譜結構? 如果一個家譜的樹結構不同於另一個的, 那麼這兩個家譜就是不同的。輸出可能的家譜樹的個數除以9901的餘數。

輸入輸出格式

輸入格式:

兩個空格分開的整數, N和K。

輸出格式:

一個整數,表示可能的家譜樹的個數除以9901的餘數。

輸入輸出樣例

輸入樣例#1: 

5 3

輸出樣例#1: 

2

有時候計算一下無用的狀態反而是有用的。例如樓下的全都是設dp[i][j]表示i個點剛好j層的方案數,然後弄個4層迴圈,還有組合數什麼亂七八糟的,不僅思維難度高,程式設計難度高,時空複雜度都高!

既然設剛好j層那麼麻煩,我們不妨設dp[i][j]表示i個點小於等於j層的方案數,那麼最終我們所需的答案就是dp[n][k]-dp[n][k-1]是不是?

考慮一下dp過程(一般樹形揹包,除非是多叉樹用分組揹包只能用dfs寫,否則可以先考慮寫一個記憶化搜尋,因為記憶化搜尋雖然效率低一些,但是思維複雜度較低,初始化考慮也會更全面,然後對應的再改寫成dp)

列舉一個t,表示分t個點給左子樹,剩下i-t-1(除去當前的根)分給右子樹,然後乘法原理搞一搞。

即:dp[i][j]=sigma(dp[t][j-1]*dp[i-t-1][j-1]),是不是很簡單?

考慮一下初始化:把dp[1][]都設成1就好了,然後列舉點的個數時只需要列舉奇數(這個很容易想到)。

參考程式碼:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int Mod=9901;
int dp[210][110],n,k;
int main(){
    scanf("%d%d",&n,&k);
    for (int i=1;i<=k;i++)dp[1][i]=1;
    for (int tk=1;tk<=k;tk++)
        for (int i=3;i<=n;i+=2)
            for (int j=1;j<i;j+=2)
                (dp[i][tk]+=dp[j][tk-1]*dp[i-j-1][tk-1])%=Mod;
    printf("%d",(dp[n][k]-dp[n][k-1]+Mod)%Mod);
    return 0;
}