【树形DP】P1352 没有上司的舞会
题目传送门
分析:
题目里明确告诉了你上司与下属的关系是一颗树,所以这一题是很明显的树形DP,而因为下属和直接上司不能同时参加,所以问题转换成一棵树上相邻的节点不能同时取,求所取点和的最大值。
那么我们可以自上而下地思考,对于一个节点,自身有取或不取两种情况,对应这两种情况的子树情况也不一样,可以分类讨论:
- 如果这个节点自身取,则他的直接儿子都不能取
- 如果这个节点自身不取,则他的儿子可以取,也可以不取(我一开始接触的时候总是写成一定要取)
那么我们如果用表示x节点不取,
表示节点x取,那么状态转移方程可以写出
其中i是x的直接儿子
其中i同上,a[i]是第i个人的幽默值。
代码
当然,有根树还要先找到根,然后进行dfs
#include <bits/stdc++.h>
#define MAX 6005
using namespace std;
int n, a[MAX], f[MAX][2];
vector<int> t[MAX];
int dfs(int x, int op){ //op==0不选, op==1选
if(f[x][op] > 0){
return f[x][op];
}
if(t[x].size() == 0){
if(!op) {
f[x][op] = 0;
return 0;
}
else {
f[x][op] = a[x];
return a[x];
}
}
int s1 = a[x], s2 = 0;
for (int i = 0; i < t[x].size(); ++i) {
s1 += dfs(t[x][i], 0);
}
for (int i = 0; i < t[x].size(); ++i) {
s2 += max(dfs(t[x][i], 0), dfs(t[x][i], 1));
}
f[x][0] = s2;
f[x][1] = s1;
return f[x][op];
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
int l , k, root, vis[MAX] = {false};
for (int i = 1; i < n; ++i) {
scanf("%d %d", &l, &k);
vis[l] = true;
t[k].push_back(l);
}
for (int i = 1; i <= n; ++i) {
if(!vis[i]){
root = i;
break;
}
}
int ans = max(dfs(root, 1), dfs(root, 0));
cout << ans << endl;
return 0;
}