1. 程式人生 > >codeforces 9D How many trees?(DP,注意狀態表示方法)

codeforces 9D How many trees?(DP,注意狀態表示方法)

題目連結

分析:比較一下各種狀態表示,

①dp[n][h] 若表示n個節點深度為h,需要列舉左右兒子的深度,則每次轉移需要O(n*h^2),不夠優; 

②若dp[n][h]表示n個節點深度大於等於h,轉移時的條件是至少有一個兒子的深度大於等於h-1,發現轉移略複雜,是:[“左兒子深度<h-1" * "右兒子深度>=h-1"]  +   [“左兒子深度>=h-1” * "右兒子深度<h-1"]  +  ["左兒子深度>=h-1" * "右兒子深度>=h-1"]  ,這三種情況的組合,深度小於h可以用“深度>=h” - "深度>=0" 代替 ,每次轉移需要O(n) , 時間效能良好, 但編寫略複雜;

③dp[n][h]表示n個節點深度小於等於h,此時答案為 dp[n][n] - dp[n][h-1] , 狀態轉移條件是兩個兒子深度都小於等於h-1 , 只有一種情況,沒次轉移需要O(n) 

同過上面比較可知第三種表示方法,兼有時間複雜度和程式碼複雜度的優勢。

附第三種方法的程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL dp[40][40];

LL DP(int n,int h){
    if(dp[n][h]>=0) return dp[n][h];
    if(h==0) return dp[n][h] = n<=0 ? 1 : 0;
    if(n==0) return dp[n][h] = h>=0 ? 1 : 0;
    dp[n][h] = 0;
    for(int k=1;k<=n;k++){
        dp[n][h] += DP(k-1,h-1)*DP(n-k,h-1);
    }
    return dp[n][h];
}

int main()
{
    memset(dp,-1,sizeof(dp));
    int n,h;
    while(cin>>n>>h) cout<<DP(n,n)-DP(n,h-1)<<endl;
    return 0;
}