cogs 106. [NOIP2003] 加分二叉樹(區間DP)
106. [NOIP2003] 加分二叉樹
★☆ 輸入文件:jfecs.in
輸出文件:jfecs.out
簡單對比
時間限制:1 s 內存限制:128 MB
【問題描述】
設 一個 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
思路:區間DP,和那道石子合並有點類似。
f[i][j]記錄區間i到j的最大值,root[i][j]記錄此時的根是幾。
那麽狀態轉移方程就可以很輕易地求出來:f[i][j]=max(f[i][j],f[i][k-1]*f[k+1][j]+num[k]),順便記錄root[i][j]=k;
最後再跑一邊先序遍歷即可。
錯因:數組初始化應該從0開始。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 31 using namespace std;long long f[MAXN][MAXN]; int n,num[MAXN],root[MAXN][MAXN]; void dfs(int l,int r){ if(l>r) return ; cout<<root[l][r]<<" "; dfs(l,root[l][r]-1); dfs(root[l][r]+1,r); } int main(){ freopen("jfecs.in","r",stdin); freopen("jfecs.out","w",stdout); scanf("%d",&n); 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++){ scanf("%d",&num[i]); f[i][i]=num[i]; root[i][i]=i; } 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]+num[k]>f[i][j]){ root[i][j]=k; f[i][j]=f[i][k-1]*f[k+1][j]+num[k]; } cout<<f[1][n]<<endl; dfs(1,n); }
cogs 106. [NOIP2003] 加分二叉樹(區間DP)