题面
题目描述
设一个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
题解
分析问题
从题目看出在构建加分二叉树的过程中,一个节点的分数最优时,他的子树的分数也一定最优。于是我们想到了动态规划——树形DP。
而我们知道一棵二叉树的中序遍历中,父亲节点的儿子一定在他两旁,所以我们可以想出如下状态:
f(i,j)表示中序遍历中i~j位能达到的分数最大值
目标状态为f(1,n)
状态转移方程:
初始:
转移:
至此,题目解决
代码
#include<iostream>
#include<cstring>
using namespace std;
int n;
int best[40][40],root[40][40];
bool first;
void visit(int i,int j)
{
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);
}
int Search_Best(int l,int r)
{
int i;
int 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.in","r",stdin);
freopen("binary.out","w",stdout);
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;
}
态度决定极限