树上dp-没有上司的舞会
题目来自算法竞赛进阶指南。
可以看作一棵树,每个节点都有权重;且选了父节点就不能选子节点,求最大值。
可以用 f [ u ] [ 0 ] f[u][0] f[u][0] 表示节点 u u u 不被选择的情况下,由 u u u 作为父节点的完整子树(包括 u u u)可达到的最大值,用 f [ u ] [ 1 ] f[u][1] f[u][1] 表示节点u被选择时子树可达到的最大值。
单独研究一个父节点和其引申出的所有子节点,如图:
第一种情况,选择了父节点
fa
\text{fa}
fa;那么它的所有子节点
S
i
S_i
Si依题意都不能被选择。故
f
[
fa
]
[
1
]
=
∑
i
f
[
S
i
]
[
0
]
;
f[\text{fa}][1]=\sum_i f[S_i][0];
f[fa][1]=i∑f[Si][0];第二种情况,不选择父节点,那么其子节点就没有任何约束。对每个子节点,都需要选择最好的方案(选这个子节点收益最大还是不选的收益最大);因此
f
[
fa
]
[
0
]
=
∑
i
max
(
f
[
S
i
]
[
0
]
,
f
[
S
i
]
[
1
]
)
;
f[\text{fa}][0]=\sum_i \text{max}(f[S_i][0],f[S_i][1]);
f[fa][0]=i∑max(f[Si][0],f[Si][1]);在实际程序中,首先使用vector用来储存树的结构;其次使用
d
f
s
dfs
dfs 来找到以任意节点为父节点所引申的子树的最大值(包括节点)。而在每次
d
f
s
dfs
dfs 结束后,最后使用后序
d
p
dp
dp 来更新父节点
u
u
u 的值。
关键代码片段:
void dfs(int u)
{
f[u][1]=w[u]; //选的情况先加上父节点权重
for(int i=0;i<son[u].size;i++)
{
int y=son[u][i]; //取出一个子节点
dfs(y); //搜索以这个子节点为父节点的子树大小
f[u][1]+=f[y][0], f[u][0]+=max(f[y][0],f[y][1]); //用搜索结果更新父节点的值
}
}