DP总结(二,树形DP)

树形DP

2024 11月 25 周一
655 字 · 4 分钟

0.定义

顾名思义就是在树上的 DP。

1.转移方式

这种 DP 不能用普通的方式转移,一般是通过 DFS 来实现转移,因为他的状态转移方程一般和子结点有关,所以可以计算完子结点的答案后合并到父结点。

一般来说,答案是根结点的答案

但是,这个树不一定是有根树

所以有些时候得扫一遍,求出以每个结点为根的答案。

题型很多,基本可以和其他任何一种 DP 融合起来。

2.类型 & 例题

1.朴素版本

Eg.1 最大子树和

对于一条边,有两种决策:保留或者计入子树答案

所以,有 DP 方程:

dpu=vson(u)max{dpv,0}dp _ u = \sum _ {v \in son(u)} max\{dp _ v,0\}

意思就是,看是否保留子树的答案,如果子树答案小于 00,则一定劣于不选该子树,所以可以这么写。

代码实现

2.树上背包(有依赖的背包)

填了 24.11.19 背包 DP 的坑

其实就是在树上的分组背包,模板如下

void DP(int u,int fa)
{
for(auto v:g[u])
{
if(v == fa)
continue;
DP(v,u);
for(int j = m;j >= 0;j--)
{
for(int k = 0;k <= j;k++)
dp[u][j] = max(dp[u][j],dp[u][j - k] + dp[v][k] + val);
}
}
}

上述代码中,jj 表示给以这个结点为根的子树分配多少容量,kk 表示给该结点的子结点为根的子树分配多少容量。

Eg.2 二叉苹果树

可以选子树中的边的充分条件是选择该条边,所以说上面的 jj 只能遍历到 11。同时,转移方程为 dpu,j=max(dpu,j,dpu,jk1+dpv,k+wuv)dp _ {u,j} = max(dp _ {u,j},dp _ {u,j - k - 1} + dp _ {v,k} + w _ {u \to v})

所以 DP 转移代码如下:

void DP(int u,int fa)
{
for(auto x:g[u])
{
int v = x.first;
if(v == fa)
continue;
DP(v,u);
for(int j = m;j >= 1;j--)
{
for(int k = 0;k < j;k++)
dp[u][j] = max(dp[u][j],dp[u][j - k - 1] + dp[v][k] + x.second);
}
}
}

代码实现

3.换根DP

就是根结点的修改会导致一些变化。

Eg.3 STA-Station

首先,我们需要一遍 DFS 来处理子树大小。

然后,令 dpudp _ u 为当以 uu 为根时,结点的深度和。

所以,有 DP 方程:

dpv=dpu+n2×sizevdp _ v = dp _ u + n - 2 \times size _ v

因为:

  1. vv 的子树上的结点深度减少 11,深度和减少了 sizevsize _ v
  2. 不在 vv 的子树上的结点深度增加了 11,则深度和减少了 nsizevn - size _ v

所以最后再扫一一遍就可以求出答案了。

代码实现

我觉得就差不多了


Thanks for reading!

DP总结(二,树形DP)

2024 11月 25 周一
655 字 · 4 分钟