1. 程式人生 > >樹形DP初步-二叉樹1661

樹形DP初步-二叉樹1661

樹形DP初步-二叉樹

時間限制: 1000 ms 記憶體限制: 65536 kb 總通過人數: 146 總提交人數: 156

 

題目描述

一道簡單的'偽'樹形dp。
注意題目難度與順序無關,請自行決策,不要卡在一道題目上太久。

關於二叉樹:

二叉樹的遞迴定義:二叉樹要麼為空,要麼由根結點,左子樹,右子樹組成。左子樹和右子樹分別是一棵二叉樹。

請注意,有根樹和二叉樹的三個主要差別:

  1. 樹的結點個數至少為1,而二叉樹的結點個數可以為0;

  2. 樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為2;

  3. 樹的結點無左、右之分,而二叉樹的結點有左、右之分。

關於最長鏈:

最長鏈為這棵二叉樹中一條最長的簡單路徑,即不經過重複結點的一條路徑。可以容易證明,二叉樹中最長鏈的起始、結束結點均為葉子結點。

現給出一棵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儲存樹結構。