洛谷P1040 加分二叉樹(樹形dp)

2022-07-01 15:09:12 字數 3979 閱讀 3757

時間限制: 1 sec  記憶體限制: 125 mb

提交: 11  解決: 7

設乙個n個節點的二叉樹tree的中序遍歷為(l,2,3,...,n),其中數字1,2,3,...,n為節點編號。每個節點都有乙個分數(均為正整數),記第j個節點的分數為di,tree及它的每個子樹都有乙個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:

subtree的左子樹的加分×subtree的右子樹的加分+subtree的根的分數

若某個子樹為主,規定其加分為1,葉子的加分就是葉節點本身的分數。不考慮它的空子樹。

試求一棵符合中序遍歷為(1,2,3,...,n)且加分最高的二叉樹tree。要求輸出:

(1)tree的最高加分

(2)tree的前序遍歷

第1行:乙個整數n(n<30),為節點個數。

第2行:n個用空格隔開的整數,為每個節點的分數(分數<100)。

第1行:乙個整數,為最高加分(結果不會超過4,000,000,000)。

第2行:n個用空格隔開的整數,為該樹的前序遍歷。

5

5 7 1 2 10

145

3 1 2 4 5

提高組-2023年noip

題目型別:

樹形dp

思路:首先,我們要做的就是設計狀態,其實就是設計dp陣列的含義,它要滿足無後效性。關注這個 左子樹*右子樹+根 我只要知道左子樹分數和右子樹分數和根的分數(已給出),不就可以了嗎?管他子樹長什麼樣!

所以,我們f陣列存的就是最大分數,怎麼存呢?

我們發現:子樹是乙個或多個節點的集合。

那麼我們可不可以開乙個f[i][j],f[i][j]來表示節點i到節點j成樹的最大加分呢?可以先保留這個想法(畢竟暫時也想不到更好的了)。

如果這樣話,我們就來設計狀態轉移方程。按照剛剛的設計來說的話,我們的答案就是f[1][n]了,那麼我們可以從小的子樹開始,也就是len,區間長度。有了區間長度我們就要列舉區間起點,i為區間起點,然後就可以算出區間終點j。通過加分二叉樹的式子我們可以知道,二叉樹的分取決於誰是根,於是我們就在區間內列舉根k。

特別的,f[i][i]=a[i]f[i][i]=a[i],其中a[i]為第i個節點的分數。

因為是要求最大值,所以我們就可以設計出f[i][j]=max(f[i][k-1]*f[k+1][j]+f[k][k])f[i][j]=max(f[i][k−1]*f[k+1][j]+f[k][k])於是乎,我們就自己設計出了乙個dp過程,因為是順著來的,所以很少有不成立的。

至於輸出前序遍歷,我們再設計乙個狀態root[i][j]來表示節點i到節點j成樹的最大加分所選的根節點。所以我們按照$根->左->右$的順序遞迴輸出即可。

#includeusing

namespace

std;

const

int maxn = 50

;typedef

long

long

ll;ll n;

//f[i][j]表示i到j子樹的最大分數

ll f[maxn][maxn],root[maxn][maxn];

void

print(ll l, ll r)

intmain()

for(int len=1;len//

以子樹的長度遍歷,1個節點的子樹,2個節點的子樹。。。

if(f[k+1][j]==0

)

if(f[i][j]}}}

cout

<1][n]

1, n);

return0;

}

posted on 2019-04-26 23:19

收藏時間限制: 1 sec  記憶體限制: 125 mb

提交: 11  解決: 7

設乙個n個節點的二叉樹tree的中序遍歷為(l,2,3,...,n),其中數字1,2,3,...,n為節點編號。每個節點都有乙個分數(均為正整數),記第j個節點的分數為di,tree及它的每個子樹都有乙個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:

subtree的左子樹的加分×subtree的右子樹的加分+subtree的根的分數

若某個子樹為主,規定其加分為1,葉子的加分就是葉節點本身的分數。不考慮它的空子樹。

試求一棵符合中序遍歷為(1,2,3,...,n)且加分最高的二叉樹tree。要求輸出:

(1)tree的最高加分

(2)tree的前序遍歷

第1行:乙個整數n(n<30),為節點個數。

第2行:n個用空格隔開的整數,為每個節點的分數(分數<100)。

第1行:乙個整數,為最高加分(結果不會超過4,000,000,000)。

第2行:n個用空格隔開的整數,為該樹的前序遍歷。

5

5 7 1 2 10

145

3 1 2 4 5

提高組-2023年noip

題目型別:

樹形dp

思路:首先,我們要做的就是設計狀態,其實就是設計dp陣列的含義,它要滿足無後效性。關注這個 左子樹*右子樹+根 我只要知道左子樹分數和右子樹分數和根的分數(已給出),不就可以了嗎?管他子樹長什麼樣!

所以,我們f陣列存的就是最大分數,怎麼存呢?

我們發現:子樹是乙個或多個節點的集合。

那麼我們可不可以開乙個f[i][j],f[i][j]來表示節點i到節點j成樹的最大加分呢?可以先保留這個想法(畢竟暫時也想不到更好的了)。

如果這樣話,我們就來設計狀態轉移方程。按照剛剛的設計來說的話,我們的答案就是f[1][n]了,那麼我們可以從小的子樹開始,也就是len,區間長度。有了區間長度我們就要列舉區間起點,i為區間起點,然後就可以算出區間終點j。通過加分二叉樹的式子我們可以知道,二叉樹的分取決於誰是根,於是我們就在區間內列舉根k。

特別的,f[i][i]=a[i]f[i][i]=a[i],其中a[i]為第i個節點的分數。

因為是要求最大值,所以我們就可以設計出f[i][j]=max(f[i][k-1]*f[k+1][j]+f[k][k])f[i][j]=max(f[i][k−1]*f[k+1][j]+f[k][k])於是乎,我們就自己設計出了乙個dp過程,因為是順著來的,所以很少有不成立的。

至於輸出前序遍歷,我們再設計乙個狀態root[i][j]來表示節點i到節點j成樹的最大加分所選的根節點。所以我們按照$根->左->右$的順序遞迴輸出即可。

#includeusing

namespace

std;

const

int maxn = 50

;typedef

long

long

ll;ll n;

//f[i][j]表示i到j子樹的最大分數

ll f[maxn][maxn],root[maxn][maxn];

void

print(ll l, ll r)

intmain()

for(int len=1;len//

以子樹的長度遍歷,1個節點的子樹,2個節點的子樹。。。

if(f[k+1][j]==0

)

if(f[i][j]}}}

cout

<1][n]

1, n);

return0;

}

洛谷 P1040 加分二叉樹

題目描述 設乙個n個節點的二叉樹tree的中序遍歷為 1,2,3,n 其中數字1,2,3,n為節點編號。每個節點都有乙個分數 均為正整數 記第i個節點的分數為di,tree及它的每個子樹都有乙個加分,任一棵子樹subtree 也包含tree本身 的加分計算方法如下 subtree的左子樹的加分 su...

洛谷 P1040 加分二叉樹

題目描述 設乙個n個節點的二叉樹tree的中序遍歷為 1,2,3,n 其中數字1,2,3,n為節點編號。每個節點都有乙個分數 均為正整數 記第i個節點的分數為di,tree及它的每個子樹都有乙個加分,任一棵子樹subtree 也包含tree本身 的加分計算方法如下 subtree的左子樹的加分 su...

洛谷P1040 加分二叉樹

設乙個 n 個節點的二叉樹tree的中序遍歷為 1,2,3,n 其中數字 1,2,3,n 為節點編號。每個節點都有乙個分數 均為正整數 記第 i 個節點的分數為 di,tree 及它的每個子樹都有乙個加分,任一棵子樹 subtree 也包含 tree 本身 的加分計算方法如下 subtree 的左子...