说了要肝的怎么能咕咕咕呢?

不了解DP或者想从基础开始学习DP的请移步上一篇博客:DP动态规划学习笔记

这一篇博客我们将分为上中下三篇(这样就不用咕咕咕了...),上篇是较难一些树形DP,中篇则是数位和状压DP,下篇则是各种DP的优化手段。

——正片开始—— (为啥我最近的博客都喜欢写这个)

背包类树形DP,树形DP里一种很鬼畜的题目。

简单点讲就是:树上的分组背包。不知道分组背包的也请前往上一篇学习。

我们先来看一道板子题:选课

然后我们一起分析一下这道题(最好自己先想一想),由于每门课的先修课只有一门,所以我们很容易想到用树形结构储存这种关系——>只能从父节点到子节点。但是,这一题的数据会形成一个森林。想一想,为什么?

没错,可能不止一门课没有先修课。但是我们只学过树形DP,怎么办呢?简单,我们自己整一棵树出来呗。

但是原来的父子关系又不能变,怎么整呢?我们新建一个“虚拟课程”0出来,作为“没有先修课的课程”的先修课。也就是说我们把森林里的每一棵树都连到一个新的根节点——0节点上,这样我们就得到了一棵树。

这个想法是不是很妙?然后这题就转换成了一个在树上运行的分组背包。

然后我们来设状态。不会?那你别学了。上一篇博客看了没?快去看。

我们上一篇讲过了,树形DP一般以每个节点x作为第一维...

然后呢?(锤),分组背包啊。哦...

于是我们就得到状态了:设f[x,j]表示我们从以x为根的子树中选j门课能获得的最高学分。

很简单是不是?状态都出来了还不知道方程咩?那我们一起来推一推吧。

我们定义几个变量方便描述,设son(x)表示x的子节点集合,siz(x)表示x的子节点个数,如果选修x这门课,那我们就对于任意y∈son(x),可以从以y为根的的子树中选出若干门课(记为ci)来修...在满足Σci=t-1的基础上,我们尽量要修最高的学分。我们有siz(x)组物品,每组物品有j-1个,其中第i组物品的第k个物品的体积为k,价值为f[y,k],背包的总容积为j-1。

我们从每组中选出至多一个物品,即每个子节点最多转移一个状态到x。我们修完x后,还可以选j-1门课,所以我们要在“物品体积”不超过j-1的情况下,使学分(价值)最大。也就是说我们把“上多少y内的课”作为物品。

然后我们进行树形DP,得到答案:

void dp(int x){
f[x][]=;
for(int i=;i<son[x].size();i++){
int y=son[x][i];
dp(y);
for(int j=m+;j>=;t--)//虚拟节点必选,所以是m+1
for(int k=;k<=j-;k++)//组内的物品,第x组第k个物品(选k门课)的体积为k
f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
}
}

主函数部分,我们读入每个f[i][1]作为第i门课的价值——>解释:从以i为子树的课中选1门——>价值就是f[i][1]。

接下来我们还是继续讨论树形DP。

我们目前做的树形DP题都是在有根树上进行树形DP的,如果题目给定是一棵无根树呢?

难道我们需要对以每个点为根都做一次树形DP统计答案吗?当然不是,要是这么做还不如暴力呢。

一般对于这种题目,我们用二次扫描(换根法)来解决。

这种DP又被称为换根DP,什么?你想要题?

没有,(爆锤狗头)...拿去 Accumulation Degree

这好像是我第一次给POJ上的题,皮欧勾的题我都没怎么刷过,不过貌似质量挺好的。

但是我永远喜欢洛谷.jpg,emm,你说你看不懂?Chrome自带翻译,不皮了不皮了。

喏。

给你一个树形的水系(废话那么多懒得翻译了)。

有一个有n个节点,n-1条河道的树形水系,每个河道有一个最大容水量c[x][y]c[x][y]表示点x到y的最大容水量,源点可以源源不断出水,以源点作为根节点的树的叶子结点可以无限接纳水,而一个节点水的流量等于流过其儿子节点的水的流量之和,儿子节点水的流量不能超过其与父亲连边的最大容水量,询问最大的源点水流量,n≤2×10^5。

其实简单点说就是求树形结构上的最大流,但是我们不知道源点。为什么不用网络流?嘘,数据太狗了。

于是我们来快乐地DP啊。不给我源点?我每个点都DP一遍。

要是这样能过我还写它干嘛。

不过还是先讲讲怎么DP吧。假设我们知道根节点是x。

那么我们来拆问题了。你看这个问题它又大又烦,不如把它拆掉。

考虑用树形DP拆问题。对于每个节点x,我们发现它只能流向自己的子树,于是可以这样设状态,我们用f[x]表示在以x为根的子树中,以x为源点流向子树的最大流量。然后我们对于每个子树都可以拆成小子树,再拆,再拆...就到叶节点了,问题就得解了。

切,刚刚还那么傲娇的大问题现在还不是被脱的一件不剩,看到DP的魅力了吧?再大的问题,只要你是DP题,我就能把你拆掉。

然后我们就很自然的得到了转移方程式:

这里我不多写一个latex下面的就会炸不知道为什么,你们就当没看见那个error吧...抱歉抱歉

$$f[x]= \sum\limits_{\text{y∈son(x)}} \begin{cases} min(f[x],c(x,y))& \text{x=0}\\ c(x,y)& \text{x!=0} \end{cases}$$

$$f[x]= \sum\limits_{\text{y∈son(x)}} \begin{cases} min(f[x],c(x,y))& \text{x=0}\\ c(x,y)& \text{x!=0} \end{cases}$$

Latex新手写上面那个公式写了半个小时...

DP代码:

void dp(int x){
vis[x]=;
f[x]=;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(vis[y])continue;
dp(y);
if(deg[y]==)f[x]+=val(i);
else f[x]+=min(f[y],val(i));
}
}

如果我们直接枚举源点...时间复杂度是O(n^2)的,接受不了(不是我接受不了,是出题人接受不了)

(如果我自己要求,O(n^n)的算法我都开开心心用...)

于是我们使用换根DP来O(n)的解决这道题目。

我们任选一个源点作为根,记为rt。然后我们进行一次上面的DP,得到f数组。

设g[x]表示把x作为源点,流向整个水系,流量最大是多少。对于根节点rt,显然有f[rt]=g[rt]。

假设g[x]已经被正确地求出了。开始了,万能的假设法...

我们考虑一下它的子节点y,g[y]我们并不知道,我们来分析一下g[y]。

如果我们把根换成y,那么g[y]包含两部分:

1. 从y流向以y为根的子树的流量,即我们上面算出来的f[x]

2. 从y沿着原父节点x流向水系中的其他部分的流量

为什么可以像2这样流呢?因为我们把根换成y了。这是很多博客没有提到的,容易让人看得很懵逼。

我们把x作为源点的总流量是g[x],从x流向y的流量就是min(f[y],c(x,y))。

这个很好理解吧,y是x的子节点,x流向子树的最大流量是f[y],流量限制是c(x,y),哪个小流量就是哪个。

所以从x流向除了y以外其他部分的流量就是两者之差。这个也很好理解吧。

于是我们把y作为源点,先流向x,再流向其他部分的流量就是两者之差和c(x,y)之间的最小值。

同样的,对于度数为1的x节点,我们也要特判——>节点x没法流向其他地方了。

于是我们得到了g[x]的计算方法:

又要写Latex...我裂开了

$$g[y]=f[y]+ \begin{cases} min( g[x]-min( f[y], c(x,y) ), c( x,y ) )& \text{x的度数>1}\\ c(x,y)& \text{x的度数=1} \end{cases}$$

g[y]就是把根从x换成y后流量的计算结果,由于这是一个从上至下的递推方程,所以我们可以通过一遍dfs得出g数组。

又到了你们最爱的放代码时间:

void dfs(int x){
vis[x]=;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(vis[y])continue;
if(deg[x]==)g[y]=f[x]+val(i);
else g[y]=f[y]+min(g[x]-min(f[y],val(i)),val(i));
dfs(y);
}
}

解释一下,val(i)就是上面说的c(x,y),我把它当作边权存了起来。

然后我们在main函数里面这样搞就可以得到答案了。

int rt=;
dp(rt);
memset(vis,,sizeof vis);
g[rt]=f[rt];
dfs(rt);
int ans=-;
for(int i=;i<=n;i++)
ans=max(ans,g[i]);

OK,那么希望你们通过这道例题,对换根DP有了一个初步的了解。

其实换根法的思想就是通过差值来更新答案,而不必一遍遍枚举计算重复信息。

那么,我们中篇再见(咕咕咕)。

DP动态规划学习笔记——高级篇上的更多相关文章

  1. 数据库MySQL学习笔记高级篇

    数据库MySQL学习笔记高级篇 写在前面 学习链接:数据库 MySQL 视频教程全集 1. mysql的架构介绍 mysql简介 概述 高级Mysql 完整的mysql优化需要很深的功底,大公司甚至有 ...

  2. DP动态规划学习笔记

    作为考察范围最广,考察次数最多的算法,当然要开一篇博客来复习啦. 子曰:温故而知新,可以为师矣 我复习DP时有一些自己对DP的理解,也就分享出来吧. ——正片开始—— 动态规划算法,即Dynamic ...

  3. Storm学习笔记——高级篇

    1. Storm程序的并发机制 1.1 概念 Workers (JVMs): 在一个物理节点上可以运行一个或多个独立的JVM 进程.一个Topology可以包含一个或多个worker(并行的跑在不同的 ...

  4. PHP学习笔记 - 进阶篇(11)

    PHP学习笔记 - 进阶篇(11) 数据库操作 PHP支持哪些数据库 PHP通过安装相应的扩展来实现数据库操作,现代应用程序的设计离不开数据库的应用,当前主流的数据库有MsSQL,MySQL,Syba ...

  5. PHP学习笔记 - 进阶篇(10)

    PHP学习笔记 - 进阶篇(10) 异常处理 抛出一个异常 从PHP5开始,PHP支持异常处理,异常处理是面向对象一个重要特性,PHP代码中的异常通过throw抛出,异常抛出之后,后面的代码将不会再被 ...

  6. PHP学习笔记 - 进阶篇(9)

    PHP学习笔记 - 进阶篇(9) 图形图像操作 GD库简介 GD指的是Graphic Device,PHP的GD库是用来处理图形的扩展库,通过GD库提供的一系列API,可以对图像进行处理或者直接生成新 ...

  7. PHP学习笔记 - 进阶篇(8)

    PHP学习笔记 - 进阶篇(8) 日期与时间 取得当前的Unix时间戳 UNIX 时间戳(英文叫做:timestamp)是 PHP 中关于时间与日期的一个很重要的概念,它表示从 1970年1月1日 0 ...

  8. PHP学习笔记 - 进阶篇(7)

    PHP学习笔记 - 进阶篇(7) 文件操作 读取文件内容 PHP具有丰富的文件操作函数,最简单的读取文件的函数为file_get_contents,可以将整个文件全部读取到一个字符串中. $conte ...

  9. PHP学习笔记 - 进阶篇(6)

    PHP学习笔记- 进阶篇(6) 会话控制(session与cookie) 当前的Cookie为: cookie简介 Cookie是存储在客户端浏览器中的数据,我们通过Cookie来跟踪与存储用户数据. ...

随机推荐

  1. Python JSON 字符串 转 json 基本使用

    字符串 转  json import json jsonData = '{"a":1,"b":2,"c":3,"d":4 ...

  2. linux下修改jar中的文件

    解压修改后再打包 解压: jar xvf xxx.jar 打包: jar cvfm0 xxx.jar META-INF/MANIFEST.MF ./ 注: -m参数可以将 一个具体的mainfest文 ...

  3. pytest 基本用法

    1.断言用assert,可以进行==,!=,+,-,*,/,<=,>=,is True.False,is not True.False ,in ,not in 等判断. import py ...

  4. LC 989. Add to Array-Form of Integer

    For a non-negative integer X, the array-form of X is an array of its digits in left to right order.  ...

  5. opencv之调用摄像头

    基础知识 # cap.set(propId, value) # 设置视频参数: propId - 设置的视频参数, value - 设置的参数值 # cap.isOpened() 返回 true/fa ...

  6. 0.9.0.RELEASE版本的spring cloud alibaba nacos+feign实例

    这里的feign依然是原来的feign,只不过将注册中心由eureka换成了nacos.服务提供方参见0.9.0.RELEASE版本的spring cloud alibaba nacos实例,消费方跟 ...

  7. 阶段5 3.微服务项目【学成在线】_day08 课程图片管理 分布式文件系统_06-分布式文件系统研究-fastDFS安装及配置文件说明

    3 fastDFS入门 3.1fastDFS安装与配置 3.1.1 导入虚拟机 对fastDFS的安装过程不要求学生掌握,可以直接导入老师提供虚拟机. 1.使用Vmware打开虚拟机配置文件“Cent ...

  8. Qt编写自定义控件55-手机通讯录

    一.前言 前面几篇文章中的控件基本上难度系数接近0,甚至有凑控件数量的嫌疑,这次必须来一个强悍的控件,本控件难度系数在所有控件中排前五,代码量也不少,头文件都550行,实现文件1600行,为什么这么多 ...

  9. Windows .net服务器系列 wmic命令使用示例--Examples of WMIC commands for Windows .NET SERVER Family

    1.0 Method execution: NICCONFIG (Win32_NetworkAdapterConfiguration) WMIC NICCONFIG WHERE Index=1 CAL ...

  10. (十六)Centos之安装mysql

    第一步:获取mysql YUM源 进入mysql官网获取RPM包下载地址 https://dev.mysql.com/downloads/repo/yum/ 点击 下载 右击 复制链接地址 https ...