【ybtoj高效进阶5-4-1】 树上求和
题目大意:
给你一棵树,给出节点的父子关系 ,让你从中选出一些节点,让他们的和最大,然后如果选择了父节点就不能选择他们的子节点,问这个最大的和是多少?
思路:
树形DP,我们要用逆推,,从叶子节点一直处理到根节点。
我们设
f
[
i
]
[
0
]
f[i][0]
f[i][0]表示处理到第 i 个点时不选这个点的最大值,
f
[
i
]
[
1
]
f[i][1]
f[i][1]表示选了这个点的最大值,son(i)是i的子节点集合。
那么取了该节点我们就不能取它的子节点,如果没取那么就可取可不取,
所以状态转移方程是:
f
[
i
]
[
0
]
+
=
m
a
x
(
f
[
j
]
[
0
]
,
f
[
j
]
[
1
]
)
f[i][0]+=max(f[j][0],f[j][1])
f[i][0]+=max(f[j][0],f[j][1])
f
[
i
]
[
1
]
+
=
f
[
j
]
[
0
]
f[i][1]+=f[j][0]
f[i][1]+=f[j][0]
其
中
j
∈
s
o
n
(
i
)
其中j\in son(i)
其中j∈son(i)
那么最后的的答案就是
m
a
x
(
f
[
r
o
o
t
]
[
0
]
,
f
[
r
o
o
t
]
[
1
]
)
max(f[root][0],f[root][1])
max(f[root][0],f[root][1]),其中
r
o
o
t
~root~
root 是这棵树的根节点
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=6*1e3+10;
struct node
{
ll val,son[V],tot;
}tree[V];
ll n,x,y,f[V][2],fa[V];
void dp(ll now)
{
f[now][1]=tree[now].val;
f[now][0]=0;
if(tree[now].tot==0) return ;
rep(i,1,tree[now].tot) //tot[i]:i节点子节点的个数
{
ll y=tree[now].son[i];
dp(y); //逆推从后往前,从叶子节点向根节点
f[now][0]+=max(f[y][0],f[y][1]);
f[now][1]+=f[y][0];
}
}
int main()
{
scanf("%lld",&n);
rep(i,1,n)
scanf("%lld",&tree[i].val);
rep(i,1,n-1)
{
scanf("%lld%lld",&x,&y);
tree[y].son[++tree[y].tot]=x; //记录子节点
++fa[x];
}
rep(i,1,n)
if(fa[i]==0) //根节点
{
dp(i);
printf("%lld",max(f[i][0],f[i][1]));
break ;
}
return 0;
}