一、题目分析
状态表示
\(f[u][1]\)表示以\(u\)为根节点的子树并且包括\(u\)的总快乐指数
\(f[u][0]\)表示以\(u\)为根节点并且不包括\(u\)的总快乐指数
状态计算
要想求得一棵以\(u\)为根节点的子树的最大指数分为两种:选\(u\)节点或不选\(u\)节点
记点\(u\)的子节点是\(j\)
- 1.选\(u\),\(f[u][1]=Σf[j][0]\)
- 2.不选\(u\),\(f[u][0]=Σmax(f[j][1],f[j][0])\)
记根节点为\(root\)
从\(root\)开始\(dfs\)一遍即可
最后输出\(max(f[root][1],f[root][0])\)
可以参阅它的姊妹题 AcWing 323 . 战略游戏
二、实现代码
#include <bits/stdc++.h>
using namespace std;
const int N = 6010;
int h[N], e[N], ne[N], idx;
int happy[N]; //快乐值
bool st[N]; //是不是根
int f[N][2]; //dp的状态结果数组
int n;
//构建邻接表
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
//通过深度优先搜索,对树进行遍历
void dfs(int u) {
//初始值
f[u][1] = happy[u];
for (int i = h[u]; i != -1; i = ne[i]) { //遍历邻接表
int j = e[i];
dfs(j); //继续探索它的孩子,它的值是由它的孩子来决定的
f[u][1] += f[j][0]; //它选择了,它的孩子就不能再选
f[u][0] += max(f[j][0], f[j][1]); //它不选择,那么它的每一个孩子,都是可以选择或者不选择的
}
}
int main() {
//优化输入
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++)cin >> happy[i]; //读入每个的快乐值
//邻接表表头初始化
memset(h, -1, sizeof h);
//读入树
for (int i = 1; i <= n - 1; i++) {
int x, y;
cin >> x >> y;
add(y, x);
st[x] = true;
}
//从1开始找根结点
int root = 1;
while (st[root]) root++; //找到根结点
//递归
dfs(root);
//取两个
printf("%d\n", max(f[root][0], f[root][1]));
return 0;
}