codeforces 9D How many trees?(DP,注意狀態表示方法)
阿新 • • 發佈:2019-02-07
分析:比較一下各種狀態表示,
①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; }