树形DP初步-真树
题目描述
新年到了,白兔家族要搞大大的聚会。但是并不是每只白兔都是同一辈分的,于是便有一棵以老白兔为根的家族树。
每只白兔都有它们自己唯一的整数编号(范围在1到N之间),并且对应一个参加聚会所得的开心值。为了使每个参加聚会的白兔都巨开心,老白兔想让每只白兔和他的上一代白兔不会同时参加聚会。
求参加聚会的白兔获得的最大总开心值。
输入
输入的第一行是一个整数N,1<= N <= 6000
以下的N行是对应的N个白兔的开心值(开心值是一个从-128到127之间的整数)
接着是白兔的家族树,树的每一行格式如下: 每行输入一对整数L,K。表示第K个白兔是第L个白兔的上一代。 输入以0 0表示结束
输出
参加聚会的白兔获得的最大总开心值
输入样例
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
输出样例
5
AC代码
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define maxn 6007 5 using namespace std; 6 7 int n; 8 int dp[maxn][2],father[maxn]; 9 10 11 void tree_dp(int node) 12 { 13 int i; 14 15 for(int i=1;i<=n;i++) //对树遍历。树的深度游戏先搜索 16 { 17 if(father[i]==node) 18 { 19 tree_dp(i); 20 dp[node][1]+=dp[i][0]; 21 dp[node][0]+=max(dp[i][0],dp[i][1]); 22 } 23 } 24 } 25 26 int main() 27 { 28 int a,b; 29 int root=0; 30 scanf("%d",&n); 31 for(int i=1;i<=n;i++) 32 { 33 scanf("%d",&dp[i][1]); 34 } 35 scanf("%d%d",&a,&b); 36 while(!(a==0&&b==0)) 37 { 38 father[a]=b; 39 40 scanf("%d%d",&a,&b); 41 } 42 root=b; 43 //找到根节点 44 while(father[root]) 45 { 46 root=father[root]; 47 } 48 //从根节点开始dp 49 tree_dp(root); 50 printf("%d\n",max(dp[root][0],dp[root][1])); 51 }
1.求相邻节点不同时取的最大节点权值和。
2.考虑序列中相邻不同取的dp:f[i][0]表示前i个,i不取的最优值;f[i][1]表示前i个,i取的最值;
同理到树形dp:f[i][0]表示i为根的子树中,i不取的最优值;f[i][1]同理。
3.则在dfs过程中,顺道dp;
4.f[i][0] = ∑max(f[j][1], f[j][0]),j为i的儿子;
f[i][1] = a[i] + ∑f[j][0],j为i的儿子,a[i]为i自身的价值。
5.答案为max(f[root][0], f[root][1])
6.注:此题由于树的结构的特殊性,自顶向下生成时并不会产生子问题的重复计算(每棵子树只对它的父节点产生贡献,这棵子树的信息只被用到一次)