DP - 區間DP - NOIP 2003 - 加分二叉樹
設一個n個節點的二叉樹tree的中序遍歷爲(1,2,3,…,n),其中數字1,2,3,…,n爲節點編號。
每個節點都有一個分數(均爲正整數),記第i個節點的分數爲di,tree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:
subtree的左子樹的加分 × subtree的右子樹的加分 + subtree的根的分數
若某個子樹爲空,規定其加分爲1。葉子的加分就是葉節點本身的分數,不考慮它的空子樹。
試求一棵符合中序遍歷爲(1,2,3,…,n)且加分最高的二叉樹tree。
要求輸出:
(1)tree的最高加分
(2)tree的前序遍歷
輸入格式
第1行:一個整數n,爲節點個數。
第2行:n個用空格隔開的整數,爲每個節點的分數(0<分數<100)。
輸出格式
第1行:一個整數,爲最高加分(結果不會超過4,000,000,000)。
第2行:n個用空格隔開的整數,爲該樹的前序遍歷。如果存在多種方案,則輸出字典序最小的方案。
數據範圍
n<30
輸入樣例:
5
5 7 1 2 10
輸出樣例:
145
3 1 2 4 5
分析:
注意:
代碼:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=35;
int n,w[N],f[N][N],g[N][N];
void pre(int l,int r)
{
if(l>r) return ;
int root = g[l][r];
cout<<root<<' ';
pre(l,root-1);
pre(root+1,r);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
for(int len=1;len<=n;len++)
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
if(len==1)
{
f[l][r]=w[l];
g[l][r]=l;
}
else
{
for(int k=l;k<=r;k++)
{
int left = k==l ? 1 : f[l][k-1];
int right = k==r ? 1 : f[k+1][r];
int score=left*right+w[k];
if(f[l][r]<score)
{
f[l][r]=score;
g[l][r]=k;
}
}
}
}
cout<<f[1][n]<<endl;
pre(1,n);
return 0;
}