【NOIP2003】加分二叉树题解

来源:互联网 发布:ip攻击软件 编辑:程序博客网 时间:2024/05/16 00:57

题面

题目描述

设一个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)
状态转移方程:
初始:

f(i,i)=di

转移:
f(i,j)=max{f(i+1,j)+di,f(i,j1)+dj,f(i,k)f(k+1,j)+dk}

至此,题目解决

代码

#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;}     

态度决定极限

0 0