9 - 树形DP
非常哈人动态规划,使我脑子旋转。
概念
树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。
例题
没有上司的舞会
285. 没有上司的舞会 - AcWing题库
状态表示:
f[u, 0]
,所有从以u
为根节点的子树中选择,并且不选u
的方案。
f[u, 1]
,所有从以u
为根节点的子树中选择,并且选择u
的方案。
我们要求它们的最大值。
状态计算:
先递归处理所有子树S1, S2, ...
,算出f[S1, 0], f[S1, 1], ...
,然后才能得出:
f[u, 0] += max(f[S1, 0], f[S1, 1]) + max(......)
。f[u, 1] += f[S1, 1] + ...
。
参考代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Graph
{
vector<int> h, happy, hasFather, e, ne;
int idx;
void add(int a, int b)
{
hasFather[b] = 1;
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
Graph(int n) : h(n + 1, -1), happy(n + 1, 0), hasFather(n + 1, 0),
e(2 * n + 5), ne(2 * n + 5), idx(0) {}
};
void dfs(vector<vector<int>>& f, Graph& g, int root)
{
f[root][1] = g.happy[root];
for (int idx = g.h[root]; idx != -1; idx = g.ne[idx])
{
int son = g.e[idx];
dfs(f, g, son);
f[root][0] += max(f[son][0], f[son][1]);
f[root][1] += f[son][0];
}
}
int main()
{
int n;
cin >> n;
Graph g(n);
for (int i = 1; i <= n; ++i)
cin >> g.happy[i];
for (int i = 1; i <= n - 1; ++i)
{
int a, b;
cin >> a >> b;
g.add(b, a);
}
// 找根节点
int root = 1;
while (g.hasFather[root])
++root;
// 通过DFS进行树形dp
vector<vector<int>> dp(n + 1, vector<int>(2, 0));
dfs(dp, g, root);
cout << max(dp[root][0], dp[root][1]);
}