题目本体
题目名称:没有上司的误会
题源:洛谷P1352
某大学有 nn 个职员,编号为 11 到 nn。他们之间有从属关系,形成一棵以校长为根的树。现在有个周年庆宴会,每邀请一个职员都会增加一定的快乐指数 riri,但如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。请你计算,邀请哪些职员可以使快乐指数最大,并求出最大的快乐指数。
输入格式:
- 第一行是一个整数 nn。
- 第 2 到第 (n+1)(n+1) 行,每行一个整数,第 (i+1)(i+1) 行的整数表示 ii 号职员的快乐指数 riri。
- 第 (n+2)(n+2) 到第 2n2n 行,每行输入一对整数 l,kl,k,代表 kk 是 ll 的直接上司。
输出格式:
输出一行一个整数代表最大的快乐指数。
示例:
plaintext
输入:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
输出:
5
限制条件:
- 对于 100% 的数据,保证 1≤n≤6×1031≤n≤6×103,−128≤ri≤127−128≤ri≤127,1≤l,k≤n1≤l,k≤n,且给出的关系一定是一棵树。
题目思路
考虑这道题目,每个职员的决策会影响其上司和下属。我们可以只关注单向影响,即上司对下属的影响,因为这种影响是相互的。
状态定义
定义状态 f[i]:f[i] 表示第 ii 个人的位置能获得的最大快乐指数。然而,由于选择具有后效性,即一个职员的选择会影响其下属,仅用 f[i] 是不够的。
解决方案
为了解决后效性问题,我们需要增加一维状态来记录所有可能的情况。因此,状态定义为 dp[i][j],其中 i 表示职员,j 表示决策(0表示不参加,1表示参加)。
状态转移方程
-
不参加的情况:dp[i][0]=∑(max(dp[son][1],dp[son][0]))dp[i][0]=∑(max(dp[son][1],dp[son][0]))
- 如果职员 i 不参加,其下属可以自由选择是否参加。
-
参加的情况:dp[i][1]=∑(dp[son][0])+happy[i]dp[i][1]=∑(dp[son][0])+happy[i]
- 如果职员 i 参加,其下属都不能参加,只能考虑其他下属不参加的情况。
关键点分析:
- 树的存储,采用链式前向星构建和遍历树状数组,关于链式前向行可自行CSDN了解。
- 状态定义:对于每个节点,定义两个状态,
dp[i][0]表示不邀请节点 i 时的最大快乐指数,dp[i][1]表示邀请节点 i 时的最大快乐指数。 - 状态转移:对于每个节点,我们需要根据其子节点的状态来更新自己的状态。
代码详解
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 6004;
int u, v, cnt, dp[N][2], rt, n, V[N], hd[N];
bool fa[N];
struct edge {
int to, nx;
} e[N];
void add(int &u, int &v) {
e[++cnt].to = v;
e[cnt].nx = hd[u];
hd[u] = cnt;
}
void dfs(int rt) {
dp[rt][0] = 0;
dp[rt][1] = V[rt];
for(int i = hd[rt]; i; i = e[i].nx) {
int to = e[i].to;
dfs(to);
dp[rt][1] += dp[to][0];
dp[rt][0] += max(dp[to][0], dp[to][1]);
}
}
int main() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> V[i];
}
for(int i = 1; i < n; i++) {
cin >> v >> u;
add(u, v);
fa[v] = true;
}
for(int i = 1; i <= n; i++) {
if(!fa[i]) {
rt = i;
break;
}
}
dfs(rt);
cout << max(dp[rt][0], dp[rt][1]);
return 0;
}