这是一个孩子之间有分配的树形动态规划问题,一般解决的方法为左孩子右兄弟法或孩子背包分配。
左孩子右兄弟表示法中,状态设定为
F[i,j]表示以i为根的子树以及以i右边兄弟为根的子树中选j个点需要删除边的最少数量。F[i,j]还隐含了i的父亲已经被选,否则i是不可能被选的。
状态转移方程为
F[i][j] = Min
{
F[i.brother][j] + 1 (不选节点i)
F[i.child][k] + F[i.brother][j-k-1] (选节点i) 0<=k<=i子树及兄弟子树大小-1 且 k<=j-1
}
边界条件
F[i][0] = F[i.brother][0] + 1
节点0为一个虚构的节点,如果节点i没有兄弟(或儿子),则它的兄弟(或儿子)为0。
F[0][0]=0
F[0][j]=INF (j>0,INF为一个极大的值)
目标状态
左孩子右兄弟表示法的目标状态表示不很直观,一棵子树分配P个节点,需要表示成子树根第一个孩子及其兄弟分配P-1的节点。另外,如果不是根节点,还要增加1,表示与它的父节点的边也断掉。
Ans = Min{ F[根节点的第一个孩子][P-1] , F[非根节点的第一个孩子][P-1] + 1 }
如果用孩子背包分配法,状态可以表示为
F[i][j]为以i为根的子树中选择j个节点要删除边的最少数量。
状态转移方程为
F[i][j] = Min { Sigma{ F[i.child[k]][m[k]] } } 其中 sigma[m[k]] = j-1
直接在孩子中分配不易,需要再进行一次DP,运用背包的思想,设状态
G[a][b]为对于特定的i时i的前i个孩子分配j个节点的最小值
G[a][b] = Min { G[a-1][b-k] + F[i.child[a]][k] }
于是F[i][j]可以表示为 F[i][j] = G[i.childcount][j-1]
边界条件为
对于每个节点i
G[0][0]=0
G[0][j]=INF (j>0 INF为一个极大的值)
当i为叶节点时
F[i][0]=1
F[i][1]=0
目标状态
Ans = Min{ F[根节点][P] , F[非根节点][P] + 1}
对于这道题,两种方法的时间复杂度均为O(N^3)。左孩子右兄弟法的优点在于状态转移比较简单,缺点是不够直观,尤其在表示目标状态的时候,另外边界情况的考虑也比较复杂,还有就是需要额外建树。孩子背包分配的方法优点是状态直观,易于表示,容易写成非递归,缺点是状态转移较为复杂,需要用一个背包再分配一次。我个人比较倾向于孩子背包分配法,除非必须,我一般都会写成这样的。
Recent Comments