前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DP专题7 | 没有上司的舞会 洛谷1352(树形DP)

DP专题7 | 没有上司的舞会 洛谷1352(树形DP)

作者头像
ACM算法日常
发布2019-07-17 20:22:25
7310
发布2019-07-17 20:22:25
举报
文章被收录于专栏:ACM算法日常ACM算法日常

高能预警:这是一篇超过5分钟的学习文章,暑假了可以多学会

本篇继续咱们的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++编译

代码语言:javascript
复制
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <bitset>
#include <iostream>
#include <set>
#include <string>
#include <vector>
#include <string.h>
using namespace std;

#define N 6100

int h[N];
int v[N];
vector<int> 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<children[x].size(); ++i){
        int y = children[x][i];
        // 先递归计算孩子的欢乐指数
        dfs(y);
        // 加上孩子的欢乐指数
        // 上司不去,孩子有可能去
        dp[x][0] += max(dp[y][0], dp[y][1]);
        // 上司去,孩子不去
        dp[x][1] += dp[y][0];
    }
}

int main() {
#ifdef __MSYS__
    freopen("test.txt", "r", stdin);
#endif

    int n;
    scanf("%d", &n);

    // 注意下标从1开始,因为题目都是用下标来指定位置的
    for(int i=1; i<=n; ++i){
        scanf("%d", &h[i]);
    }

    for(int i=1; ; ++i){
        int c, p;
        scanf("%d%d", &c, &p);
        if(!c && !p)break;
        // 用于判断是否为根节点,只有根节点没有parent
        v[c] = 1;
        // 用二维数组记录父子关系
        children[p].push_back(c);
    }

    int root;
    for(int i=1; i<=n; ++i){
        if(!v[i]){
            root = i;
            break;
        }
    }
    // 开始树形递归搜索(线性DP都是迭代搜索)
    dfs(root);
    printf("%d\n", max(dp[root][0], dp[root][1]));
    return 0;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ACM算法日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目描述
  • 输入输出格式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档