题目描述
Ural大学有N名职员,编号为1~N。
他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。
每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。
现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。
在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。
输入格式
第一行一个整数N。
接下来N行,第 i 行表示 i 号职员的快乐指数Hi。
接下来N-1行,每行输入一对整数L, K,表示K是L的直接上司。
输出格式
输出最大的快乐指数。
数据范围
1 ≤ N ≤ 6000,
−128 ≤ Hi ≤ 127
输入样例:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
输出样例:
5
思路
这个题目是树形dp
的题目,所谓树形dp,就是在一棵树上进行,没有环,dfs不会重复。
首先就是将这个边的关系用邻接表存起来。
题目要求就是不能和直接上级一起,要么没有自己,要么没有直接上级,就相当于咱们的学生、班主任和级部主任一样,要么有学生没有班主任,要么由班主任没有学生。
假如选择了学生,班主任不能去,但是级部主任可以去。
既然是一道动态规划的问题,我们就要从大问题归结为小问题。
首先,我们来定义一个数组dp[ i ] [2]
dp[i][0] 的含义就是从以 i 为根的子树中选择,并且选 i 这个结点的方案。
dp[i][1] 的含义就是从以 i 为根的子树中选择,并且不选 i 这个结点的方案。
- 选择了 5 ,那么结点 3 、4 就不能选择,那么当前的快乐指数dp[ 5 ][ 1 ] = 5;
- 不选择了 5 ,那么结点 3 、4 就能选择,结点1、2、3、4就不能选择,那么当前的快乐指数为dp[ 5 ][ 0 ] = 2;
- 所以快乐指数最多为
max(dp[i][0],dp[i][1])
- 将子结点的最大快乐指数传到父节点,然后在传到根节点root,我们最终就是要求
max(dp[root][1],dp[root][0])
;
细节
- 我们要找到根节点root,我们可以开一个数组布尔类型的数组
isfa[i]
,如果当前结点 i 有父节点,那么isfa[i]=true
; - 将边的关系用邻接表存起来。
- 在树上进行递归搜索
代码
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 6010;
int n,root; //root 为根节点
int dp[N][2];// 为当前结点的最大幸福指数
int val[N],isfa[N];//isfa[N]为当前结点是否有父节点
int h[N],e[N],ne[N],idx;// 储存邻接表
void add(int a,int b)// 加一条边a->b
{
e[idx]=b;
ne[idx]=h[a];
h[a] = idx ++;
}
void dfs(int u)
{
dp[u][1] = val[u];//如果选择当前结点,要将当前结点的幸福指数加上
for(int i = h[u];i != -1;i = ne[i])
{
int j = e[i];
dfs(j);//对子结点进行搜索
dp[u][0] += max(dp[j][0],dp[j][1]);
dp[u][1] += dp[j][0];
}
}
void Findroot()
{
root=1;
while(isfa[root])
root++;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",val+i);
memset(h,-1,sizeof h);// 初始化头结点
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
isfa[a]=1;
add(b,a);
}
Findroot();//寻找根节点
dfs(root);
printf("%d\n",max(dp[root][0],dp[root][1]));
return 0;
}