加分二叉树 (binary)
【问题描述】
设一个 n 个节点的二叉树 tree 的中序遍历为( l,2,3,…,n ),其中数字 1,2,3,…,n 为节点编号。每个节点都有一个分数(均为正整数),记第 j 个节点的分数为 di , tree 及它的每个子树都有一个加分,任一棵子树 subtree (也包含 tree 本身)的加分计算方法如下:
subtree 的左子树的加分 × subtree 的右子树的加分+ subtree 的根的分数
若某个子树为空,规定其加分为 1 ,叶子的加分就是叶节点本身的分数。不考虑它的空
子树。
试求一棵符合中序遍历为( 1,2,3,…,n )且加分最高的二叉树 tree 。要求输出;
( 1 ) tree 的最高加分
( 2 ) tree 的前序遍历
【输入格式】
第 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
思路
{
因为是中序遍历,所以如果i为根节点,那么1..i-1是左子树,i+1..j为右子树
于是你就发现了每次的决策
所以这是一道区间DP。。。
用f[i,j]为i..j构成的树所能得到的最大分值
那么可以得到DP方程:
f[i,j]:=fen[i] | i=j
f[i,j]:=max(f[i,j],f[i,k-1]*f[k+1,j]+fen[k]) | i<=k<=j
//注意子树相乘时如果子树为0就当1 处理,
//if f[i,k-1]=0 then f[i,k-1]:=1;
//if f[k+1,j]=0 then f[k+1,j]:=1;
//例如:
// 如果左子树为0 , 那么左子树当为1 ,因为1*右子树=右子树
}
代码
1 program no; 2 const 3 inf=‘binary.in‘; 4 outf=‘binary.out‘; 5 var 6 i,j,k,n,t1,t2:longint; 7 f,root:array[0..100,0..100] of int64; 8 fen:array[0..100] of longint; 9 10 procedure outfirst(s,e:longint); 11 begin 12 if s>e then exit; 13 write(root[s,e],‘ ‘); 14 outfirst(s,root[s,e]-1); 15 outfirst(root[s,e]+1,e); 16 end; 17 18 begin 19 assign(input,inf); 20 assign(output,outf); 21 reset(input); 22 rewrite(output); 23 24 readln(n); 25 for i:= 1 to n do 26 begin 27 read(fen[i]); 28 f[i,i]:=fen[i]; 29 root[i,i]:=i; 30 end; 31 32 for i:=n downto 1 do 33 for j:= i+1 to n do 34 for k:= i to j do 35 begin 36 if f[i,k-1]=0 then f[i,k-1]:=1; 37 if f[k+1,j]=0 then f[k+1,j]:=1; 38 t1:=f[i,k-1]; 39 t2:=f[k+1,j]; 40 if f[i,j]<t1*t2+fen[k] then 41 begin 42 f[i,j]:=t1*t2+fen[k]; 43 root[i,j]:=k; 44 end; 45 end; 46 47 writeln(f[1,n]); 48 outfirst(1,n); 49 50 close(input); 51 close(output); 52 end.