Bootstrap

「2003NOIP提高组」加分二叉树

Time Limit:1000 ms                                          

Memory Limit:65536 K


CodeVS[1090]:


题目描述 Description

设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为ditree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:

subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数

若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空

子树。

试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;

1tree的最高加分

2tree的前序遍历

 

 

现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入描述 Input Description

1行:一个整数nn<=30),为节点个数。

2行:n个用空格隔开的整数,为每个节点的分数(分数<=100

输出描述 Output Description

1行:一个整数,为最高加分(结果不会超过4,000,000,000)。

2行:n个用空格隔开的整数,为该树的前序遍历。

样例输入 Sample Input

5

5 7 1 2 10

样例输出 Sample Output

145

3 1 2 4 5

数据范围及提示 Data Size & Hint

nn<=30)

分数<=100


算法讨论

本题一看题目描述就知道这题必须要建立一个树的数据结构,而最后要让求最大值,那么就只能用动态规划了,因为如果用深搜之类的肯定是浪费时间太多,每个点枚举一边,额时间是问题。

那么我们已经确定是树形动态规划了,就需要建立一棵树,那么题目让求最大值就说明一定要枚举每一个点当root,因为输入是中序遍历,所以假设枚举的一个根是3号点,那么他的左子树是12号点,右子树是4..n号点,那么这给点的最优值是1号点或二号点乘上4..n+上本身,那么左子树的点和右子树的点又得枚举了,然后递归知道还有最后一个点的时候。要注意的是判定边界和记忆化的问题,记忆化可以使你的代码更快,每一棵树少算一遍。

那么既然是动态规划,就要有状态转移方程了,每个点的最优值就等于左子树点的最优值乘上右子树点的最优值+本身,这就是f数组。

状态转移方程:f[r,l]:=max{f[i,k] * f[k,j]+a[i]}(for k:=r to l do)

 

Pascal代码:

var
  a:array[0..1000] of longint;                   {存下每一个点的分数}
  f:array[0..1000,0..1001] of int64;             {存下最优分数,因为数据原因所以用int64,中括号里存左儿子点和右儿子点}
  root:array[0..1000,0..1000] of longint;        {最后为了输出的问题,还需要先序遍历,所以存一下根,左子树点右子树点}
  n,i:longint;
 
function treedp(l,r:longint):int64;              {递归函数,把上一次递归的边带进来了}
  var
    i:longint;
    max,t:int64;
  begin
    max:=0;                                      {要把每一个子树的最大值存一下}
    if f[l,r]>=0 then exit(f[l,r]);             {记忆化,算过了就不要再算了直接用}
    if l=r then 
      begin 
        f[l,r]:=a[l]; 
        root[l,r]:=l; 
        exit(f[l,r]); 
      end;                                       {边界处理问题,别出边!}
    if l>r then 
      begin 
        f[l,r]:=1;
        exit(f[l,r]);
      end;                                       {这个点没有右兄弟,所以分是1}
    for i:=l to r do                             {枚举第i个点为根}
      begin
        t:=treedp(l,i-1)*treedp(i+1,r)+a[i];     {递归进去}
        if t>max then 
          begin 
            max:=t;
            root[l,r]:=i;
          end;                                   {状态转移方程,如果这个点的值大于最大那么替换,还要把根的位置存一下}
      end;
    f[l,r]:=max;                                 {把最大存入f数组}
    exit(f[l,r]);                                {结束递归}
  end;
 
procedure qianxu(l,r:longint);                   {前序遍历过程}
  begin
    if l>r then exit;
    if (l=1) and (r=n) then 
      write(root[l,r]) 
    else 
      write(' ',root[l,r]);
    qianxu(l,root[l,r]-1);
    qianxu(root[l,r]+1,r);
  end;
 
begin
  readln(n);
  for i:=1 to n do read(a[i]);
  fillchar(f,sizeof(f),255);                    {先把f初始化成-1,这是为了记忆化}
  writeln(treedp(1,n));
  qianxu(1,n);
  writeln;
end.






;