zoukankan      html  css  js  c++  java
  • 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]的最大值即可。

    总结

  • 相关阅读:
    Java实现 洛谷 P1060 开心的金明
    (Java实现) 洛谷 P1605 迷宫
    (Java实现) 洛谷 P1605 迷宫
    (Java实现)洛谷 P1093 奖学金
    (Java实现)洛谷 P1093 奖学金
    Java实现 洛谷 P1064 金明的预算方案
    Java实现 洛谷 P1064 金明的预算方案
    (Java实现) 洛谷 P1031 均分纸牌
    QT树莓派交叉编译环开发环境搭建(附多个exe工具下载链接)
    武则天红人对唐睿宗的桃色报复(如此缺少城府,注定了要在宫廷中过早地出局)
  • 原文地址:https://www.cnblogs.com/xiaoyezi-wink/p/11314880.html
Copyright © 2011-2022 走看看