1. 程式人生 > >cogs 106. [NOIP2003] 加分二叉樹(區間DP)

cogs 106. [NOIP2003] 加分二叉樹(區間DP)

cstring def n) -- efi 其中 spa times oid

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)