洛谷 P1352 没有上司的舞会
题目链接:没有上司的舞会
思路
这是一道树形dp的入门题,也可以用DFS+记忆化搜索来理解,要注意的地方是,输入关系时先输入的l为后输入的k的下属,所以存储边时需要注意。
在面对图论或者树的存储时通常可以使用链式向前星(如下代码中的add函数)或者STL中的vector存储边。
链式向前星和vector数组存储的差别在于,链式向前星的时间效率会比vector数组要高,而且vector数组存储边时的空间消耗可能也会比链式向前星要高,因为vector每次扩充容量时默认是申请当前容量的1.5倍或者2倍,所以可能在面对一些题目时会浪费很多的空间,被卡内存,所以在面对知道边数的题目时,尽量使用链式向前星会节省时间和空间效率。
使用happy数组存储每个员工的快乐指数,subordinate数组标记当前元素是否是下属,因为题目没给出根节点是哪个节点,所以需要自己找出根节点,由于给出的数据结构是一颗树只有一个根节点,所以可以逐个标记下属subordinate[i] = true
,最后没有被标记的节点subordinate[i] == false
就是根节点。
dp数组存储的是当前节点及其子树最大的快乐指数,dp[i][0]表示不邀请职员i时,职员i及其所有直接和间接下属能得到的最大的快乐指数,dp[i][1]表示邀请职员i时,职员i及其所有直接和间接下属能得到的最大的快乐指数。
搜索中要求出dp[i][0]和dp[i][1]需要先得到下属对应子树的最大快乐指数,所以需要先dfs在加上下属对应子树的最大快乐指数。
题解
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
int dp[N][2], happy[N], subordinate[N], cnt, head[N], nex[N], edge[N];
// 链式向前星存储边
void add(int x, int y) {
nex[++cnt] = head[x];
head[x] = cnt;
edge[cnt] = y;
}
// 树形dp
void dfs(int x) {
dp[x][0] = 0;
dp[x][1] = happy[x];
for (int i = head[x]; i; i = nex[i]) {
int to = edge[i];
dfs(to);
dp[to][1] += dp[to][0];
dp[to][0] += max(dp[to][0], dp[to][1]);
}
}
int main() {
int n, l, k;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> happy[i];
}
for (int i = 1; i < n; i++) {
cin >> l >> k;
add(k, l);
subordinate[l] = true;
}
int root;
for (int i = 1; i <= n; i++) {
if (subordinate[i] == false) {
root = i;
break;
}
}
dfs(root);
cout << max(dp[root][0], dp[root][1]);
return 0;
}