背景
(CCF) (NOIP2003) (T3), (Luogu) (P1040)
题意
设一棵二叉树(形态未知)的中序遍历是 ({ 1,2,3,cdots,n }) 。给定这 (n) 个节点的权值,规定一棵子树的得分是左右子树得分之积加上根节点权值的结果。请构造出一棵满足题意的树使得得分最大,并求出最大得分和树的先序遍历。注意叶节点的得分是本身权值,仅有一棵子树的节点另一棵空子树得分记为 (1) 。
解法
不是树形 (dp) 模板。是习题。
然而我死活想不出解。后来发现我把中序遍历和先序遍历搞混淆了。
由于构造出来的树要满足中序遍历的要求,则选定 (x) 点为子树 ([l,r]) 的根之后左子树只能为 ([l,x-1]) ,右子树只能为 ([x+1,r]) 。于是就有了最优子结构性质。
设 (f_{l,r}) 表示中序遍历是 ({ l,l+1,l+2,cdots,r }) 的子树的最大得分。则转移应当枚举其中的一个点作为这棵子树的根,再递归计算左右子树的值。
于是考虑记忆化搜索(本质是动态规划的递归实现形式),状态转移是 (f_{l,r}=max_limits{k in [l,r]} { f_{l,k-1} imes f_{k+1,r}+val_k }) 。要求的就是 (f_{l,r}) 。
最后考虑怎么输出先序遍历。由于先根、后左子、最后右子,于是在 (dp) 的时候可以给每棵子树 ([l,r]) 确定一个根节点 (root_{l,r}) ,做完之后从 ([1,n]) 开始递归输出即可,先输出根节点,再递归左子树,最后递归右子树。
细节
(1.) 两个边界:子树为空(即 (l>r) )时显然得分只能为 (0) ;子树仅有一个节点(即 (l=r) ,是一个叶节点)时显然得分就是本身权值。
(2.) 注意输出时为了避免递归不停止,在子树为空(即 (l>r) )时直接 (return) 。