p1040 加分二叉樹
我們知道中序遍歷相同的二叉樹可能有很多種,請在滿足給定的中序遍歷的二叉樹中,找出得分最小二叉樹,輸出它的得分和前序遍歷。
得分規則:樹的得分 = 左子樹的得分 * 右子樹的得分 + 根節點的得分。
特別的,若沒有左(右)子樹,則左(右)子樹得分為1。
若為葉子節點,則得分就是葉子節點的分數。
注:題目中給出的中序遍歷是每個結點上的分數,並非結點編號,輸入序號即為結點編號。
需要輸出的前序遍歷中,要的是結點編號,而非結點分數。
搜尋所有可能的二叉樹,得到最優的結果。
搜尋問題
我們知道中序遍歷是由三部分組成,即左子樹,根結點,右子樹。當我們選擇了根節點之後,左邊的就是左子樹的中序遍歷,右邊的就是右子樹的中序遍歷。
如果我們不考慮左子樹,右子樹它們各自的內部結構,那麼對於一棵二叉樹而言,把根節點的所有可能都搜尋一邊,那就相當於把這棵樹所有可能都搜尋一邊。借用遞迴思想,我們分別對左右子樹進行相同的操作,那最終就可以真正得把所有可能的二叉樹都搜尋完畢。
記憶化搜尋問題
這裡建立dp陣列,就是把當前情況計算後的結果儲存下來,當下一次再遇到同樣情況時,可以直接獲取結果,避免重複搜尋計算。
例如對於總序列[1,2,3,4,5]
中的子串行[1,2]
。
我們會在以3為一級根節點時,[1,2]
會被作為一級左子樹計算一遍。
當我們以4作為一級根節點時,一級左子樹成為了[1,2,3]
,如果此時我們再以3作為二級根節點,[1,2]
會被作為二級左子樹再被計算一遍。
這裡的作為二級左子樹的[1,2]
和上面作為一級左子樹的[1,2]
,在計算結果上顯然沒有什麼不同。這種情況就是我們所說的重複情況。
前序遍歷
在上面的搜尋計算的過程中,我們可以得到在序列區間[a,b]
n內讓哪個結點作為根節點為最優,我們記錄下來,最後再利用遞迴方法,把前序序列列印出來。
#include
using
namespace std;
int n;
int d[30]
,sp[30]
[30]=
;unsigned dp[30]
[30]=
;unsigned
intdfs
(int begin,
int end)
if(dp[begin]
[end]!=0
)return dp[begin]
[end]
;unsigned
int _max =
1, tmp, node;
//遍歷根節點的所有可能
for(
int i = begin;i<=end;i++)}
sp[begin]
[end]
= node;
dp[begin]
[end]
= _max;
return _max;
}void
print
(int begin,
int end)
int node = sp[begin]
[end]
; cout << node +
1<<
" ";
print
(begin,node-1)
;print
(node+
1,end);}
intmain()
P1040 加分二叉樹
設乙個n個節點的二叉樹tree的中序遍歷為 1,2,3,n 其中數字1,2,3,n為節點編號。每個節點都有乙個分數 均為正整數 記第i個節點的分數為di,tree及它的每個子樹都有乙個加分,任一棵子樹subtree 也包含tree本身 的加分計算方法如下 subtree的左子樹的加分 subtree...
P1040 加分二叉樹
設乙個nn個節點的二叉樹tree的中序遍歷為 1,2,3,n1,2,3,n 其中數字1,2,3,n1,2,3,n為節點編號。每個節點都有乙個分數 均為正整數 記第ii個節點的分數為di,treedi,tree及它的每個子樹都有乙個加分,任一棵子樹subtreesubtree 也包含treetree本...
P1040 加分二叉樹
題目 很明顯的一道區間dp題目。和acwing上的金字塔有點像。表面上感覺是樹形dp,實則不然。題中給出的1 n是中序遍歷,按照區間dp的套路 設f l r 為中序遍歷是l r的子樹的最大加分值。那麼劃分點佷顯然就是列舉一顆子樹的根節點,再加上這棵樹是一顆二叉樹就更好辦了。dp方程如下 for in...