本篇继续咱们的DP专题,树形DP入门。动态规划每一个类型的DP都是深坑,期望童鞋们自己在这个系列的基础上多花时间进行拓展,学习愉快~
在讨论树形DP之前,我想介绍一个比较有名的学习技巧——费曼技巧,因为个人觉得可以尝试着用在咱们的算法理解上。费曼这个人本身是一个很有意思的人,做科研和教育都非常厉害,另外后人还根据他的个人经历拍了一部爱情片,是不是跨越有点大,好了,先说费曼定理。
2012年,加拿大人斯科特.H.杨(Scott H Young)用一年的时间自学完成了MIT公开课上正常情况下需要四年才能修完的计算机科学的33门课程,并最终通过了所有考试。在分享其学习方法的时候,他提到了费曼技巧,从此,费曼技巧进入了公众视野。
那么,费曼技巧到底是什么呢?
在费曼的自传里,他提到曾纠结于某篇艰深的研究论文。他的办法是,仔细审阅这篇论文的辅助材料(supporting material),直到他掌握了相关的知识基础、足以理解其中的艰深想法为止。
费曼技巧,也可以称为四步学习法:
第一步 选择一个你想要理解的概念
第二步 设想一种场景,你正要向别人传授这个概念
第三步 如果你感觉卡壳了, 就回顾一下学习资料
第四步 为了让你的讲解通俗易懂,简化语言表达
最终的目的, 是用你自己的语言, 而不是学习资料中的语言来解释概念。
先看到这里,下周会专门发一篇费曼技巧的文章。接着来看树形DP题目。
现学现用,思考一下树形DP的费曼解释
假设你要对一个是7岁小学1年级学生讲解,要怎样解释树形DP呢?
首先假设这个小学生已经了解了DP(1年级能理解DP也是厉害了),那么可以说:
树形DP就是基于树形搜索进行状态转移的DP。
好吧,还是很有难度的,有兴趣的童鞋可以留言说出你的想法。
因为树形天生具有递归的性质,树形DP一般都是通过深度优先搜索进行迭代,从叶子节点回溯到根节点的过程。
你可以想象自己在一棵倒挂树的叶子节点上,一点点往根部爬。之前线性DP我们都是从f[0]这样的位置一点点往f[n]这样递推,树形DP是换了一种方式,从线性数组到树形。
某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式:
第一行一个整数N。(1<=N<=6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0 0
输出格式:
输出最大的快乐指数。
本题是树形DP的经典入门题目,因为理解起来没那么复杂,转移方程如下图所示:
只用考虑上司去和不去2中状态,进行状态转移。
源代码:G++编译
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 6100
int h[N];
int v[N];
vector children[N];
int dp[N][2];
void dfs(int x)
{
// 对于叶子节点来说
// 不去为0分
dp[x][0] = 0;
// 去的话初始化为自己的欢乐指数
dp[x][1] = h[x];
for(int i=0; i
下期预告:DP专题 8 | 状态压缩DP
往期回顾
DP专题 6 | 石子合并 CH5301(区间DP)
DP专题 5 | 颜色的长度 - UVA1625(线性DP)
DP专题 4 | 骨头收集爱好者 - POJ 1458( 0-1背包)
DP专题 3 | LCS最长公共子序列 - POJ 1458
DP专题 2 | LIS POJ - 2533(经典DP)
DP专题 1 | 数字三角形 - POJ 1163(简单DP)
前缀和、二维前缀和与差分的小总结
个人公众号(acm-clan):ACM算法日常
专注于基础算法的研究工作,深入解析ACM算法题,五分钟阅读,轻松理解每一行源代码。内容涉及算法、C/C++、机器学习等。