DP&图论 DAY 2 下午
基础树形DP
前言
◦ 1:与树或图的生成树相关的动态规划。
◦ 2:以每棵子树为子结构,在父亲节点合并,注意树具有天然的子结构。
这是很优美的很利于dp的。
◦ 3:巧妙利用Bfs或Dfs序,可以优化问题,或得到好的解决方法。
◦ 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] 表示 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为根的子树内选取不相交树链的价值和的最大值,
枚举一条LCA为x 的链(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数组。
◦递归计算son的dp值,传入的参数是dpson数组。
◦回溯回u点
◦对每一个i做dp[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
每一个基环树最大直径可能过环可能不过环
过环的话找每个环端点上的树的最长分支之后处理找最长
不过环的话每个端点上的树求树的最长直径
◦先找出环,很明显答案有两种情况。
◦ 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]的最大值即可。
总结