題目描述
【問題描述】
設一個 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 的前序遍歷
輸入格式 1794.in
第 1行:一個整數 n ( n < 30 ),爲節點個數。
第2 行: n 個用空格隔開的整數,爲每個節點的分數(分數<100 )。
輸出格式 1794.out
第 1行:一個整數,爲最高加分(結果不會超過 4,000,000,000 )。
第2 行: n 個用空格隔開的整數,爲該樹的前序遍歷。
輸入樣例 1794.in
5
5 7 1 2 10
輸出樣例 1794.out
145
3 1 2 4 5
這題首先要讀懂題意。一開始我還把輸入的分數誤認爲中序遍歷。。。。後來發現中序遍歷是1 2 3 4 5。就是要算加分最多的樹的分數和先序遍歷。
代碼無比友善,一看就懂,但是要想出來,好吧,我還是想不出來。(⊙o⊙)…
這題的狀態的確定是非常重要的,我覺得這個狀態也挺特別。f[i][j]代表,從第i個結點到第j個結點的數的最大加分。
枚舉一個i和j,i從大到小,j從小到大。我和wyy還討論過爲啥i要從大到小,反過來變成i從小到大,j從大到小不行嗎。後來她解釋說,如果這樣的話,範圍就變成1~n,問題就變大了。
然後枚舉一個根節點k
f[i][k-1]*f[k+1][j]+f[k][k]>f[i][j]那就賦值,用一個數組b[i][j]記錄下從i到j得出最大值的根節點,方便後面的輸出。
要做一點預處理,首先,把所有的f[i][j]都賦爲1,如果沒有左子樹時,那就只用右子樹加根節點,如果f[i][j]是0,一乘,得出來的結果就是0了,而不是我們所要的右子樹加根節點。其次,把f[i][i]=a[i],b[i][i]=i這個我就不用解釋了。
一開始的時候,我陷入了死循環,因爲我忘記給b初始化了。然後後來一交,竟然是0分,發現我輸出的格式是不對的,本來兩個結點之間有一個空格,我打了兩個。。。腦殘無比。
#include
#include
using namespace std;
int n,f[35][35],b[35][35],a[35];
void DP()
{
for(int i=n;i>=1;i--)
{
for(int j=i+1;j<=n;j++)
{
for(int k=i;k<=j;k++)
{
if((f[i][k-1]*f[k+1][j]+f[k][k])>f[i][j])
{
f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];
b[i][j]=k;
}
}
}
}
}
void first(int l,int r)
{
if(l<=r)
{
cout<>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++) f[i][j]=1;
}
for(int i=1;i<=n;i++)
{
f[i][i]=a[i];
b[i][i]=i;
}
DP();
cout<