这几天,我自学了基础的树形DP,在此给大家分享一下我的心得。
首先,树形DP这种题主要就是解决有明确分层次且无环的树上动态规划的题。这种题型一般(注意只是基础、普通的情况下)用深度优先搜索来解决实际的DP部分,而且一般用记忆化搜索,因为树会有重复遍历节点的情况。在一棵树上针对普遍子树都满足根节点与两个子节点有特殊关系的,树形DP就应该是解决这道题的优选方法了。
讲完基础的定义,我们来讲几个挺有用的性质:
1、在一棵二叉树上,已知其中序遍历,它的任一个节点都可以成为它的根节点,对于每一个子树都成立。
2、在二叉树上任一个节点出发,都能遍历每一个位置。而且每个节点与另一个节点只有一条枝条连通。
3、树中一定无环。
4、多叉树转变成的二叉树中,原来的兄弟节点的兄弟还是自己的兄弟,孩子的孩子也还是自己的孩子的孩子,但是兄弟的孩子与自己无关,而且兄弟和自己是具有同等发展机会的。
5、树是具有递归性质的,无论是建树还是遍历,递归都有效。
6、……
各位大神如果有什么好的性质可以补充的话,欢迎在评论中补充。
接下来,这个存储树的方式也很讲技巧。判断节点数多不多,很少的话建议用邻接矩阵。
假如边数太多的话而且边与边之间联系比较大的话,建议用邻接表(链式前向星,数据大小要开2*n)。或者还可以用专门存树的各种方法,例如树根孩子表示法,双亲表示法,孩子兄弟表示法(重点)……
最后讲讲做这种题的步骤:
1、读题,分析。判断这道题到底是不是树形DP,还是披着树的外套的普通区间DP,还是其他树的问题。假如不是,马上找其他的方法。
2、想想怎么建树。首先该用什么来存储,判断这是一棵怎样的树。是单纯二叉树呢,或者单纯多叉树,还是多叉树转二叉树呢,这都需要经验判断,不同的题不一样。建树的过程可以用一个DFS(普通二叉树),也可以用建图的方法(多叉树),还可以用brother和child的方法(多叉转二叉)。
3、列出状态转移方程,并且用记忆化搜索来实现。选择返不返回值的时候因情况而定。
4、检查边界情况和初始化够不够周全,通常会在这里失分。还要想想有没有必要优化和有没有优化的方法,例如用指针存储等等。
讲几道例题:
1、加分二叉树
这道题分析后发现它并不是树形DP,只是有树的外形的区间DP,所以不满足第一步判断,撤。不过他的输出先序遍历还是挺值得理解一下的。
详情请看:http://www.cnblogs.com/Ronald-MOK1426/p/8445336.html
2、二叉苹果树
这道题是最基础的二叉树版树形DP。他用树根孩子表示法,很简单解决。
注意建树的时候记得删边,而且最后输出的是q+1。
详情请看:http://www.cnblogs.com/Ronald-MOK1426/p/8446738.html
3、最大利润
这道题是我们学校初三的一位大佬推荐的,尽管我们并没有做过(是JZOJ的题),但是也是多叉树中的一道好题。注意是双向边。有兴趣的同学可以去搜一下。
4、没有上司的舞会
这道题也是多叉树的一道经典题目,经过无数次改编。要用邻接表来储存。但是要注意初始化的时候小心点。后面用取点还是不取点的方法取到最大值。
详情请看:http://www.cnblogs.com/Ronald-MOK1426/p/8449790.html
5、选课
这道题是一道多叉树转二叉树的经典题。要用孩子兄弟表示法来存储。是一道有依赖性的问题。我比较看好他的改编版,要是我们要输出取了那些课程,那么这道题就变难了许多。我们可以通过原路返回的思想,一路找回相等的数值,那么就可以推出答案。同时也可以边记忆化搜索边推答案。
详情请看:http://www.cnblogs.com/Ronald-MOK1426/p/8448397.html
6、软件安装
这道题是选课的一个升级版,假如大家对选课非常熟悉和理解透彻的话,相信理解起来不是问题。但是初始化有点麻烦。
因为题目的意思是可能会出现互相依赖的情况,所以这道题是有环的。所以我们就要加入判环和缩点两个步骤。把判出来的环缩成一个新点。之后就和选课别无他样了。
详情请看:http://www.cnblogs.com/Ronald-MOK1426/p/8449686.html
说了这么多,总结一下。
这只是基础的树形DP,往下学肯定有各种的优化,本人在此先不作讲解,水平有限,见谅。
要是算过内存或者数组会爆的话,可以用链表、指针来优化一下存储的环节。
记忆化搜索部分一定要注意边界条件,否则会出错。
最后,讲一个自己总结的树形DP的套路。要是我们判断了一道题是树形DP,那么一般只可能3种存储:邻接矩阵、邻接表、孩子兄弟。然后记忆化部分就是简单的判断有无出现过,到边界了没有,假如都不满足,就搜索下一层,通过不断减少体积(分体积)的方法求出最值。(普通DP的基本操作)
请各位大神指正,谢谢大家!