DP&图论 DAY 2 下午

DP&图论  DAY 2  下午

基础树形DP

前言
1:与树或图的生成树相关的动态规划。
2:以每棵子树为子结构,在父亲节点合并,注意树具有天然的子结构。
这是很优美的很利于dp的。
3:巧妙利用BfsDfs序,可以优化问题,或得到好的解决方法。
4:可以与树上的数据结构相结合。
5:树形Dp的时间复杂度要认真计算,部分问题可以均摊复杂度分析。
6:一般设f[u]表示u子树的最优价值或者是说方案数。
或者f[u][k]表示u子树附加信息为k的最优值,往往是通过考虑子树根节点
的情况进行转移。

7:树形dp,在树结构上,求最优值,求方案等dp问题,就可以考虑是树
dp
当然也有可能是点分治或者是分析性质的贪心题。但是树形dp绝对是一
个很好的思考方向。

 

>树上最大独立集

给你一棵大小为n的树,求这棵树的最大独立集是多少。
最大独立集指的是一个最大的点集使得其中的点两两没有边相连。
N<=100000

>Solution

dp[i][0/1]表示做完了i的子树, i点是否选的最大独立集,即可直接转移。

对于当前节点 i ,如果不选 i ,那么他的儿子可以选也可以不选

如果选 i ,那么他的儿子一定不能选

代码还是很好写的。


>树的直径
给你一颗点数为n的树,让你求这棵树的直径是多少,也就是求最长的两
个点之间的距离。
N<=100000
>Solution

1:设 f[i] 表示 这个点到子树里面的最远点是多远的,然后对于每一个点u
求出以这个点为根的最远路径距离,直接找 {f[son_i]+edge_i} 的前两大
值加起来即可。然后再在每一个点构成的答案里面取最大值就是全局的
最优值了。

 

2:随便找一个点bfs求它的最远点,设为x,再从x跑一遍bfs,求x最远点y
(x,y)就是一个直径了。
考虑离每一个点最远的点肯定是直径的其中一个端点。

 其他的一些简单问题
1:一棵无向树,结点为n(<=10,000),删除哪些结点可以使得新图中每一
棵树结点数小于等于n/2。也就是求重心。
2:树的覆盖集,求最少选几个点能覆盖所有边,也就是不存在一条边两
边点都没被选。(本质?)
3:最大权独立集?
其实都是一样的。

覆盖集和独立集是互补的,一起构成了树的全集

>Tree chain problem
给定一棵有n 个点的树,以及m 条树链,其中第i 条树链的价值为wi,请
选择一些没有公共点的树链,使得价值和最大。
1:n,m<=1000

>Solution

考虑树形DP,设f(x)为以x为根的子树内选取不相交树链的价值和的最大值,
枚举一条LCAx 的链(u,v,w),那么当前方案的价值为w+ 去除u v 路径上
的点后深度最小的点的f的和。
复杂度O(M*N)
树链剖分优化可以做到O(M*log(n)^2)

对于当前点 i ,如果 i 不在树链上,那么树链就在他的子树里面

如果 i 在树链上,如图紫色部分,那么我们还可以在绿色部分找到别的树链

>BZOJ1864 三色二叉树
给出了一棵二叉树,点数为n,然后要求每个点和其左儿子和其右儿子三
者两两之间颜色互不相同,求最多能有多少点被染成绿色。
N<=10^5

>Solution

 f[i][0] f[i][1]f[i][2]分别表示根是绿红蓝三种颜色时的最多/最少绿色的
数量,转移的时候只要保证上面的约束就行,并不难。

 

bzoj2466
图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯
 和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点
 亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。
 并且该节点的直接邻居也发生同样的变化。
 开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按
 钮,才能让所有节点的指示灯变为点亮状态。
对于100%的数据,满足1 <= n <=100

>Solution

f[u][0/1][0/1]  自己亮没亮  能不能按灯

f[u][0][0]=sigema f[j][1] [ 0 ](按了偶数次

g[0]+f[i][0][0] --> g[0] 偶数个孩子按,最少操作次数

      +f[i][1][1] --> g[1] 奇数个孩子按,最少操作次数

g[1]+f[i][1][1] --> g[0]

      +f[i][1][0] --> g[1]

状态:
f[pos][bool] 表示按下这个pos点的按钮之后,这个点亮(或不亮),它的所
有子孙都亮的最小代价。
g[pos][bool]表示不按这个点的按钮,这个点亮(不亮),它的所有子孙都亮
的最小代价。

树上背包

>树上背包简化版
给出一棵n个点的有根树,每个节点都是一个物品,具有价值Vi,如果一
个物品要被选择,那么它的父亲必须被选择。
求选择至多m个物品的最大价值和。
n<=1000
>Solution

f[i][j]表示在以i为根子树中选择, i强制选择,选择j个点的最大价值,转移
时每次将一个孩子暴力合并到父亲上,合并就枚举这个孩子内部选择了
多少点即可。
F[i][j]=max{f[i][j-k]+f[son][k] |k=0…(j-1)},就是枚举son内选了多少点。
我们按照一般的分析复杂度的方式的话是:状态数N^2*转移复杂度N,总
复杂度是O(N^3)
实际上我们考虑每次合并的时候相当于是一组点对数量的复杂度,总的
来看的话就是n个点点对的数量,均摊复杂度O(N^2)

>bzoj4033: 树上染色
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要
在这棵树中选择K个点,将其染成黑色,并
将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的
距离加上白点两两之间距离的和的收益。
问收益最大值是多少。
N<=1000
>Solution

f[u][j]u子树j个黑点  u子树的最大收益  ,  j*(k-j) 

考虑边的贡献:两端黑点数量的乘积+两端白点数量的乘积。
定义状态f[i][j]表示i号节点为根节点的子树里面有j个黑色节点时最大的贡
献值。
然后我们要知道的就是i到其父亲这条边会计算次数就是: 子树中白色节
点数子树外白色节点数+子树中黑色节点数子树外黑色节点数。

这条边的贡献和i的各个孩子的子树内各有多少黑点是无关的,所以我们
可以做背包求出来每个点子树内有j个黑点时贡献和是多少。
代码如下 红线部分必须按图示这个写法,保证N^2的均摊复杂度分析

 

>树上背包
给出一棵n个点的有根树,每个节点都是一个物品,具有价值Vi和重量Wi
如果一个物品要被选择,那么它的父亲必须被选择。
求限制重量m内的最大价值和。
n<=1000,m<=1000
>Solution

>最朴素的做法
这里不是选点的数量而是重量,所以这里的朴素做法是O(n^3)
f[i][j]表示在以i为根子树中选择, i强制选择,重量为j的最大价值,转移时
每次将一个孩子暴力合并到父亲上,合并就枚举这个孩子内部选择了多
少的重量即可。
F[i][j]=max{f[i][j-k]+f[son][k] |k=0…(j-1)},就是枚举son内用了多少重量。
注意我们这里两个一维数组的背包合并是n^2的,所以慢。
但一个一维数组和一个单独的物品合并是线性的。

>DFS序上做DP
dfs序上Dp,如果不选一个点,则跳过代表他的子树的dfs上连续一段。
f[i][j]表示dfs序上第i个点到第n个点,选了j的重量得到的最大的价值是
多少。 i可以选也可以不选。不选的话就要跳过整个子树。
T[i]表示dfs序为i的点标号。
不选: f[i + size[ T[i] ] ][j]
选: f[i+1][ j- w[ T[i] ]]+v[ T[i] ]
两种情况取最大值即可。

>另一个奇妙的方法
不是每次将孩子与自己合并,我们直接把dp数组复制再传给孩子,再从
孩子递归下去,最后原来自己的Dp数组和孩子的Dp数组直接在对应重量
的价值中取max
以下是步骤:
我们现在在u节点,对u节点的dp数组中加入u点的物品。
dp[i]=dp[i-w[u]]+v[u]操作,表示强制加入了u这个物品。
Dpson数组=dp数组。
递归计算sondp值,传入的参数是dpson数组。
回溯回u
对每一个idp[i] = max{dpson[i],dp[i]}

>bzoj5123
求一棵 [1,n] 的线段树的最大匹配数目与方案数。
N<=10^18

f[u][0]=max(f[l][0],f[l][1])+max(f[r][0],f[r][1])

树形dp+记忆化搜索
f[l][r] 表示根节点为 [l,r] 的线段树,匹配选择根节点的最大匹配&方案
数, g[l][r] 表示根节点为 [l,r] 的线段树,匹配不选择根节点的最大匹配&
方案数。那么这是一个很普通的树形dp
注意到区间长度相等的线段树的结果是一样的,且每层至多有两种区间
长度不同的区间(打表或者推推式子都行),因此直接以区间长度为状态进
行记忆化搜索即可。

 基环树

 >基环树
基环树,也是环套树,简单地讲就是树上再加一条边。
如果把那个环视为中心,可看成:有一个环,环上每个点都有一棵子树的形式。
因此,对基环树的处理两部分分别为对树处理和对环处理。

>基环树问题处理方法
处理方法有
1:断开环上一条边,枚举边端点的情况,然后当树来处理。
2:先把环上挂着的树的信息都计算完,然后转化为序列问题,或者说是环形的序列问题。 


 >dfs找环
基环树,环是关键,所以做这类题目我们首先得找到环。
找环的方式很多,这里讲解dfs找环。
对于dfs找环,我们就对这个基环树做dfs遍历。

我们知道对于一个在图,它dfs树上的每一个返祖边(v-->u)
dfs树上构成的路径就会构成一个环。也就是我们只需要找到这个返祖边即可。


主函数调用时,要枚举每一个点。

因为有可能是个基环树森林。

这是很容易犯的一个坑: n个点n条边不一定是个基环树,准确来讲是基环树森林!! 


如果说我们要采用断开一条边,当成树来处理。我们不需要找出来整
个环,只需要找一个在环上的边,按下图写法会简便很多。 

环的一个边的两个结点是 rt1 , rt2

>基环内向树

首先它是一个有向图,它构成类似基环树的结构,有一个特点是每个点
都有且只有一个出度,并且环外的节点方向指向环内。
如果题目说满足每一个点都有一个唯一出度,则本质上就是给了我们一个基环内向树
森林(不只是一个基环内向树!!!!)
任何一个点沿着唯一出边走都会走到环上
利用这个性质可以随便选一个点再通过一
个简单循环找到基环树的环。

 >基环外向树
与基环内向树相反,它有且只有一个入度(基环内向树是出度),并且
并且由环指向环外。
可以把所有边反向后,变成基环内向树找快速找环。

>bzoj1040
N个人,每个人都有一个战斗力和一个讨厌的人(不是他本身),要求一
个总战斗力最大的人的集合,满足集合内部两两不互相讨厌
N<=10^5
>Solution

基环树的树上最大独立集

 把这个讨厌关系的图画出来,就是个基环内向树森林,然后我们要求最
大权独立集。(当然也可以是外向树,关键看咋连边)
求最大独立集内向和外向和无向图毫无区别,都是相邻的不能选。
这里的基环树上有且仅有一个环,就是从任意环上一条边(u,v)断开环,分
两种情况,一种是选u,不选v,一种是选v,不选u,两种情况取最大值。
转化成树的话,就是那个简单的树形dp
找环dfs找就好,或者从一个点顺着父亲一直走直到走到一个曾经走到过
的点就找到一个环了。



 >BZOJ1791
求无向基环森林中的每棵基环树的直径之和。
点数n<=10^6

>Solution

 

qwq: 要统计森林的直径和要找每一个 基环树的直径
每一个基 环树最大直径可能过环可能不过环
过环的话找每个环端点上的树的最长分支之后处理找最长
不过环的话每个端点上的树求树的最长直径

先找出环,很明显答案有两种情况。
1:在以一个环上节点为根的向外的树的直径。
2:以两个环上节点分别为根的最大深度再加上两个节点在环上距离。
第一种情况就是之前讲的树形dp
第二种情况要处理出以环上每个节点为根的最大深度d[i],环上的点重标
号,选环上1号点作为基准点,求出s[i]表示i号点到1号点的距离, sum
总的环长。

设我们找的两个环上节点是i,j
max( min{s[i]-s[j],sum-(s[i]-s[j]} +d[i]+d[j])即为所求,

    取最大(取最小(i,j两点在环上的距离)+ 一个点到 i 的距离 + 一个点到 j 的距离)

但如果暴力求是n^2的。并没有比最开始直接枚举快多少。
考虑优化。

我们考虑把内部的一个min去掉,式子能看起来更清晰一些。
max( min{s[i]-s[j],sum-(s[i]-s[j])} +d[i]+d[j])
Max( 1: s[i]-s[j] +d[i]+d[j] | s[j]>=s[i]-sum/2,
2: sum-s[i]+s[j] +d[i]+d[j] | s[j]< s[i]-sum/2 )
考虑枚举选的两个点的后一个点i,然后求对于i,离i最远的j距离是多少,然后
对于每一个i的答案求最大值就是整个基环树的直径了。
第一种情况s[j]>=s[i]-sum/2 :求d[j]-s[j]的最大值即可,注意可选的j区间会移动,
所以这里需要单调队列。
第二种情况s[j]< s[i]-sum/2:这个可行区间只会变大,不会缩小,所以直接记录
s[j]+d[j]的最大值即可。

总结

猜你喜欢

转载自www.cnblogs.com/xiaoyezi-wink/p/11314880.html
今日推荐