树形动态规划(Tree DP)是动态规划(DP)的一种特殊形式,它专门用于解决树形结构上的问题。在树形DP中,我们通常使用递归的方法来遍历树的节点,并在每个节点上计算一些基于其子节点信息的值。
凡是牵涉到树的题目,一般都会用到dfs(爆搜),或者bfs(宽搜)。本题会用到dfs,忘记的话可以向前复习:DFS
题目含义:
在不选择其直接上司的前提下求最大的happy值,转化到图上:
若选择根节点5,则4和3不能选(因为5是4、3的直接父节点),6、7、1、2可以选(因为5不是6、7、1、2的直接父节点)。
若选择了父节点4,那么5不能选(5是4的直接父节点);6、7不能选(4是6、7的直接父节点)......
思路:
状态表示:集合:①f[u][0]所有从以u为根的子树中选择,并且不选u这个点的方案
②f[u][1]所有从以u为根的子树中选择,并且选择u这个点的方案
属性:MAX
状态计算:当集合为①时,不选择u这个根节点,那么根节点u的所有子节点可以选or不选,取一个最大值即可:f[u][0] += Σmax(f[j][1],f[j][0])
当集合为②时,选择u这个根节点,那么根节点u的所有子节点都不能选,那么f[u][1] += Σf[j][0]。
遍历每一个根节点的所有子节点时通常采用爆搜的方式(dfs),我们在dfs中处理根节点是,往往是先处理好与根节点所有相连的子节点,而每一个子节点的状态即为f[ j ][0],f[ j ][1]。
一开始从根节点进行向下爆搜,而根节点无上司,所以找到一个没有上司的点即为根节点
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 6010;
int n;
int happy[N];//每一个人的高兴度
int f[N][2];//选or不选的状态
int idx, e[N], ne[N], h[N];//图——注意本题是有向图,所以不存在双向指向。所以不需要额外的st数组判断是否被遍历过
bool whether_st[N];//寻找根节点的bool数组
void add(int a, int b)//b加到a的后面
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u)
{
f[u][1] = happy[u];//选择u作为根节点时,它的基础happy值为happy[u]
for(int i = h[u]; i != -1; i = ne[i] )
{
int j = e[i];
dfs(j);//爆搜根节点u的所有子节点
f[u][1] += f[j][0];//选根节点u
f[u][0] += max(f[j][0], f[j][1]);//不选根节点u
}
return;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> happy[i];
memset(h, -1, sizeof h);
n -= 1;
while(n -- )
{
int a, b;
cin >> a >> b;
add(b, a);//子节点a应该在父节点b之后
whether_st[a] = true;//说明a有上司
}
//找根节点
int root = 1;
while(whether_st[root]) root ++ ;
//从根节点开始遍历的原因是因为每一个点只会被遍历一次,即爆搜到底
dfs(root);
//输出状态①和②的最大值即可
cout << max(f[root][1], f[root][0]) << endl;
return 0;
}
本题即是简单的dp+dfs处理+树的建立。不过合在一块,若有一个部分卡住,那么可能就不简单了😂