首先,什么是树形DP?树形DP,就是一种利用树的特殊结构,来进行一些DP的规划性题目。一般来说,可以用在树上的规划问题或者说计数问题上。
一般来说,树形DP的基本架构是以某个节点作为DP的一维,接着设某个状态为以root为根节点的子树的某个规划,接着,对于每一个状态,一般来说都是可以从root的所有孩子的状态来进行转移的。
那么,为什么树形DP会正确呢?这可以很容易证明。首先,DP有一个无后效性和子问题的划分,而树形的结构而言,DP的划分非常清晰。首先是无后效性,那么因为树上没有环,所以不可能有一个状态被多次转移。而子问题的划分,可以轻而易举地划分成多棵子树。所以在树上DP是很简单的。
那么,我们如何安排这一DP的顺序,使得当我们DP某个节点的时候,其所有的孩子节点都被DP过了?一般的答案是DFS。如果我们利用DFS,先将某个节点的孩子节点DFS掉,再DP当前的节点。很显然,因为这个节点的所有孩子节点都被DFS过了,所以说其所有的孩子节点都被DP过了。那么这完全可以做到。
但是考虑另外一个细节,DFS很可能会爆栈!因为我们用DP的时候,一般来说都是基本的方法都做不了的,要用DP来优化。所以,我们要用到BFS。DFS如果手写栈的话,还不如直接用广搜。因为广搜的效率比较高,而深搜则多次回溯,不及广搜的剪枝。所以说,我们用广搜更好。
广搜之后怎样再做呢?考虑广搜“一层一层”的结构,我们首先可以任选一个点,开始BFS,按照访问到的顺序依次编为1,2,3,4……n,然后从第n个节点开始逆序向前不断进行DP的转移。最后就可以达成与DFS相似的效果了。
这种做法的效率比起DFS更为提高,而且完全不会有爆栈的风险。如果还是担心的话,我看还是循环队列完事(不过应该完全不会有这样的情况,除非内存不够大)。