樹形DP初步-二叉樹1661
樹形DP初步-二叉樹
時間限制: 1000 ms 記憶體限制: 65536 kb 總通過人數: 146 總提交人數: 156
題目描述
一道簡單的'偽'樹形dp。
注意題目難度與順序無關,請自行決策,不要卡在一道題目上太久。
關於二叉樹:
二叉樹的遞迴定義:二叉樹要麼為空,要麼由根結點,左子樹,右子樹組成。左子樹和右子樹分別是一棵二叉樹。
請注意,有根樹和二叉樹的三個主要差別:
-
樹的結點個數至少為1,而二叉樹的結點個數可以為0;
-
樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為2;
-
樹的結點無左、右之分,而二叉樹的結點有左、右之分。
關於最長鏈:
最長鏈為這棵二叉樹中一條最長的簡單路徑,即不經過重複結點的一條路徑。可以容易證明,二叉樹中最長鏈的起始、結束結點均為葉子結點。
現給出一棵N(N<=100000)個結點二叉樹,問這棵二叉樹中最長鏈的長度為多少,保證了1號結點為二叉樹的根。
輸入
輸入第1行為包含了一個正整數N,為這棵二叉樹的結點數,結點標號由1至N。
接下來N行,這N行中的第i行包含兩個正整數l[i], r[i],表示了結點i的左兒子與右兒子編號。如果l[i]為0,表示結點i沒有左兒子,同樣地,如果r[i]為0則表示沒有右兒子。
輸出
輸出包括1個正整數,為這棵二叉樹的最長鏈長度。請注意,鏈長定義為經過的邊的個數。
輸入樣例
6
2 3
4 5
0 6
0 0
0 0
0 0
輸出樣例
4
AC程式碼
#include <iostream> #include<algorithm> #include<cstdio> using namespace std; const int maxn=100010; int l[maxn],r[maxn]; int g[maxn]; int f[maxn]; int tmax=-1; void DFS(int x){ if(x!=0){ DFS(l[x]); DFS(r[x]); g[x]=max(g[l[x]],g[r[x]])+1; } else{ g[x]=0; } } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&l[i],&r[i]); } DFS(1); for(int i=1;i<=n;i++){ f[i]=1+g[l[i]]+g[r[i]]; if(f[i]>tmax){ tmax=f[i]; } } printf("%d",tmax-1); }
1.鏈所含節點數容易表示,先計算節點數-1即為邊數。
2.最長鏈的端點一定是葉結點。
3.對於一個樹形dp,通常常見的狀態轉移是從兒子節點,整合到自身;同時考慮自身貢獻給父親節點的資訊。
4.最終的答案可能是根節點的dp資訊,也可能是所有節點資訊的綜合
5.f[i]表示從子樹經過點i的最長鏈點數是多少,g[i]表示從點i為根的子樹中,以i為端點的最長鏈的點數:
那麼,f[i]=1+g[left_son]+g[right_son],
g[i]=max(g[left_son]+g[right_son])+1(狀態轉移方程),
答案就是max{ f[i] }
5.用DFS後續遍歷,自底向上生成陣列g。
遍歷到節點i時i的左右孩子的g都已經得到。
6.用兩個陣列l,r儲存樹結構。