DP动态规划学习笔记——高级篇上

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

不了解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][0]=0;
    for(int i=0;i<son[x].size();i++){
        int y=son[x][i];
        dp(y);
        for(int j=m+1;j>=1;t--)//虚拟节点必选,所以是m+1
            for(int k=0;k<=j-1;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的最大容水量,源点可以源源不断出水,以源点作为根节点的树的叶子结点可以无限接纳水,而一个节点水的流量等于流过其儿子节点的水的流量之和,儿子节点水的流量不能超过其与父亲连边的最大容水量,询问最大的源点水流量,n2×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]=1;
    f[x]=0;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(vis[y])continue;
        dp(y);
        if(deg[y]==1)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]=1;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(vis[y])continue;
        if(deg[x]==1)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=1;
dp(rt);
memset(vis,0,sizeof vis);
g[rt]=f[rt];
dfs(rt);
int ans=-1;
for(int i=1;i<=n;i++)
    ans=max(ans,g[i]);

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

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

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

 

posted @ 2019-11-09 20:42  LightHouseOfficial  阅读(...)  评论(...编辑  收藏