题目描述
设一个n 个节点的二叉树T 的中序遍历为(1,2,3,...,n),其中数字 1,2,3,...,n 为节点编号。
每个节点都有一个分数(均为正整数),记第j 个节点的分数为dj。二叉树T 及它的每个子树都有 一个加分,任意一棵子树S(包括T 本身)的加分等于S 的左子树的加分×S 的右子树的加分+S的根的分数。 若某棵子树为空,规定其加分为1。叶子的加分就是叶节点本身的分数,不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,...,n)且加分最高的二叉树T。要求输出T 的最高加分和前序遍历。
输入格式
第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
解:从题目的描述中,我们可以看出,在求T 的最高加分的过程中,有很多求各棵子树加分的子问题,且易知这些子问题也同样满足最优性要求的。由于在中序遍历中,同一棵子树的各个节点的编号是连续相邻的,我们只要指定编号的起始位置和终止位置就可以把一个子问题描述完整。因此,本题我们可以采用动态规划的思想加以解决。设besti,j表示从第i个节点开始到第j个节点为止的子树加分的最高值,
备忘录法.cpp
#include<iostream>
#include<cstring>
using namespace std;
//int max_nodes=40;
int n;
long best[40][40];
int root[40][40];
bool first;
void visit(int i,int j)//当中序遍历为1,2,3,4,5。。。时,前序遍历的求法。
{
if(i>j)
return ;
if(first)
first=false;
else
cout<<" ";
cout<<root[i][j];
visit(i,root[i][j]-1);
visit(root[i][j]+1,j);
}
long Search_Best(int l,int r)
{
int i;
long now;
if(l>r) return 1;
else
{
if(best[l][r]==-1)
{
for(i=l;i<=r;i++)
{
now=Search_Best(l,i-1)*Search_Best(i+1,r)+best[i][i];
if(now>best[l][r])
{
best[l][r]=now;
root[l][r]=i;
}
}
}
return best[l][r];
}
}
int main()
{
freopen("binary.in5","r",stdin);
freopen("out3.txt","w",stdout);
while( cin>>n)
{
memset(best,-1,sizeof(best));
memset(root,-1,sizeof(root));
for(int i=1;i<=n;i++)
{ cin>>best[i][i];
root[i][i]=i;
}
cout<<Search_Best(1,n)<<endl;
first=true;
visit(1,n);
cout<<endl<<endl;
}
return 0;
}