简单了解树形DP

今天在B站看了一个树形DP教学视频有所收获,做一个小小的总结

AV号和链接在这:av12194537

 

那么先介绍一下树形DP

树形DP就是在树这个特殊的数据结构上进行的DP。有两种方向:自顶向下自底向上

树形DP运用了DFS的方式。

一般来说都是用自底向上的方向,也就是从叶子节点到根节点。如果采用自顶向下的方式,那么要先进行一遍自顶向下的DFS(预处理),再由根节点到叶子结点获取想要的答案。

 

用一个经典入门题讲解一下:没有上司的舞会

题目大意就是给一棵树每个节点给定一个价值,子节点和父节点不能同时选取,求能够选取的最大价值。

我们用dp[u][0]表示不选取u节点u子树(包括u在内)能获得的最大价值,dp[u][1]表示选取u节点u子树(包括u在内)能获得的最大价值。

w[u]表示单个u节点的价值。

我们从根节点开始进行DFS遍历,遍历完整棵树以后回溯的同时就开始自底向上获取答案。对于任意一个节点u,要得到它的包括自身的子树的价值的最大值,这个最大值需要记录两个,一个是选了自身,一个是没选自身,也就是dp[u][1]和dp[u][0]。如果没有选自身,那么每个子节点都可选可不选,u的最大值就加上每个子节点选和不选的两个最大值中较大的那个,等于Σ(max(dp[vi][0], dp[vi][1]))(vi是u的第i个字节点)。状态转移方程就是 dp[u][0] += max(dp[v][0], dp[v][1]);而如果选了u节点自身,那么它所有的子节点都不能选,最大值就等于Σ(dp[v][0]),转态转移方程就是 dp[u][1] += dp[v][0]

最后只要比较根节点的两个最大值,大的那个就是想要的结果。

上代码:

void Dfs(int u)
{
    dp[u][0] = 0;
    dp[u][1] = w[u];
    for(int v:e[u]) //用v代表根节点
    {
        Dfs(v);//因为要从叶子节点自底向上,所以先遍历一遍所有节点
        dp[u][0] += max(dp[v][0], dp[v][1]);
        //不选u节点的话子节点v可选可不选
        dp[u][1] += dp[v][0];
        //选u节点的话字节点v只能不选
    }
}

int main()
{
    ...
    Dfs(root);//从根节点开始DFS;
    maxvalue = max(dp[root][0], dp[root][1]);
    //最后比较选根节点和不选根节点哪个大
    ...
    return 0;
}

 

posted @ 2018-07-21 16:52  Lightfall  阅读(565)  评论(0编辑  收藏  举报