题目描述
某大学有 n n n 个职员,编号为 1 … n 1\ldots n 1…n。
他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。
现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 r i r_i ri
但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。
所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式
输入的第一行是一个整数 n n n。
第 2 2 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行一个整数,第 ( i + 1 ) (i+1) (i+1) 行的整数表示 i i i 号职员的快乐指数 r i r_i ri。
第 ( n + 2 ) (n + 2) (n+2) 到第 2 n 2n 2n 行,每行输入一对整数 l , k l, k l,k,代表 k k k 是 l l l 的直接上司。
输出格式
输出一行一个整数代表最大的快乐指数。
样例 #1
样例输入 #1
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
样例输出 #1
5
提示
数据规模与约定
对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 6 × 1 0 3 1\leq n \leq 6 \times 10^3 1≤n≤6×103, − 128 ≤ r i ≤ 127 -128 \leq r_i\leq 127 −128≤ri≤127, 1 ≤ l , k ≤ n 1 \leq l, k \leq n 1≤l,k≤n,且给出的关系一定是一棵树。
解题思路:
首先,注意题中说的是“如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。”
也就是说间接上司来不来是无所谓的
接下来讲解本题的解题思路
这道题可以算是树形DP的入门级题目了
树形DP,就是在树形结构中采用动态规划
(说了但好像什么都没说)
常规思路就是自顶向下递推,确定叶子节点后,逐级回归更新父节点
那么这道题为什么采用树形DP呢?
首先题目中给出的是一棵树
其次,我们想要知道舞会的最大快乐指数,除了遍历每一种可能外好像别无选择
那么降低时间复杂度就要想到DP,在本题中也就是树形DP
思路很简单,每个职员只有两种状态:来、不来
我们用dp[now][1]
表示来,dp[now][0]
表示不来
那么有
//树形DP
void dfs(int now) {
//参加
dp[now][1] = happy[now];//参加,初始化为自己的快乐指数
//不参加
for (int i = head[now]; i != -1; i = edges[i].next) {//遍历子节点
int v = edges[i].v;
dfs(v);
//返回后更新
dp[now][0] += max(dp[v][0], dp[v][1]);//不参加,需要累加子节点来的情况
dp[now][1] += dp[v][0];//参加,需要累加子节点不来的情况
}
}
最后,AC代码如下
#include <iostream>
#include <string.h>
using namespace std;
const int max_n = 6e3;
const int min_r = -128;
const int max_r = 127;
int n, u, v;
int happy[max_n + 1];
int in[max_n + 1];
//链式前向星
struct edge { int v, next; }edges[max_n + 1];
int head[max_n + 1];
int tot = -1;
//树形DP
long long dp[max_n + 1][2];
//存边
void add_edge(int u, int v) {
edges[++tot] = { v,head[u] }; head[u] = tot;
}
//树形DP
void dfs(int now) {
//参加
dp[now][1] = happy[now];//参加,初始化为自己的快乐指数
//不参加
for (int i = head[now]; i != -1; i = edges[i].next) {//遍历子节点
int v = edges[i].v;
dfs(v);
//返回后更新
dp[now][0] += max(dp[v][0], dp[v][1]);//不参加,需要累加子节点来的情况
dp[now][1] += dp[v][0];//参加,需要累加子节点不来的情况
}
}
int main() {
memset(head + 1, -1, sizeof(int) * max_n);
cin >> n;
for (int i = 1; i <= n; i++) cin >> happy[i];
for (int i = 1; i < n; i++) {//存图
cin >> u >> v;
add_edge(v, u);//反向存边,自顶向下
in[u]++;
}
for (int i = 1; i <= n; i++)
if (!in[i]) {//从根节点开始
dfs(i);
cout << max(dp[i][0], dp[i][1]) << endl;
break;
}
return 0;
}