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

不了解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. docker install and minikube install

    1.选择国内的云服务商,这里选择阿里云为例 curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/ ...

  2. 项目管理工具-OmniPlan 3 for Mac

    链接:https://pan.baidu.com/s/1tp_37fHXHwJuklL1nNSwig  密码:l0sf 激活迷药(里面自带的keygen不能用,用这个好使): Name: Appked ...

  3. Linux工程管理器——make

    一.定义 工程管理器,顾名思义,是指管理较多的文件 Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能构根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Make ...

  4. LC 456. 132 Pattern

    Given a sequence of n integers a1, a2, ..., an, a 132 pattern is a subsequence ai, aj, ak such that  ...

  5. Oracle 11g 监听很慢,由于监听日志文件太大引起的问题(Windows 下)

    现象:Windows 操作系统的Oracle 数据库,使用sqlplus 连接(不指定实例名)连接很快,程序连接或使用连接工具或在Net Manager 中测试连接都需要花费约三四十秒的时间(程序连接 ...

  6. 123457123456#0#-----com.threeapp.magicImageShow01----儿童宝宝魔法画笔

    com.threeapp.magicImageShow01----儿童宝宝魔法画笔

  7. java写文件实现换行

    Java 写文件实现换行   第一种: 写入的内容中利用\r\n进行换行 File file = new File("D:/text"); try { if(!file.exist ...

  8. PAT 甲级 1041 Be Unique (20 分)(简单,一遍过)

    1041 Be Unique (20 分)   Being unique is so important to people on Mars that even their lottery is de ...

  9. Tips for vcpkg

    概述 vcpkg是微软开发的在Windows, Linux和MacOS平台管理C/C++库的开源工具. 快速开始 要求 使用vcpkg需满足如下条件: Windows 10, 8.1, 7, Linu ...

  10. LNMP一键环境安装多PHP版本共存的方法

    多PHP版本只支持LNMP模式,LNMPA.LAMP模式下不支持!要使用多PHP先安装多PHP版本,在lnmp1.4源码(lnmp1.3的不行哦)目录下运行:./install.sh mphp 按提示 ...