动态规划模型总结之树形dp

刚讲完状压,我们来讲讲树形dp

1.基本概念

树形 d p dp dp,顾名思义就是在树上做动态规划,通过若干次的遍历树,得到有用的信息,解决问题

树形 d p dp dp的枚举顺序类似线性 d p dp dp,也有两个方向:

①:叶→根:根的子节点传递有用的信息给根,由根得出最优解

②:根→叶:需要取出所有点作为根节点求值,最后根节点获得整棵树的信息

动态规划的顺序一般是后序遍历,即当子节点处理完成再处理父节点

树形 d p dp dp一般通过记忆化搜索实现

普通的树形 d p dp dp的时间复杂度基本都是 O ( n ) O(n) O(n),若有附加维则为 O ( n ∗ m ) O(n*m) O(nm)

2.经典问题

①树的直径

给你一颗 n n n个节点的有权有根树,根节点编号为1,找到一条最长的路径

最长链不一定经过根,所以不能单纯的从根贪心

要解决这个问题我们可以使用树形 d p dp dp

d [ x ] d[x] d[x]表示从节点 x x x出发走向以 x x x为根的子树,能够到达的最远节点的距离

d [ x ] = max ⁡ i ∈ s o n [ x ] d [ i ] + l e n g t h [ x ] [ i ] d[x]=\max_{i∈son[x]}d[i]+length[x][i] d[x]=maxison[x]d[i]+length[x][i]

答案就是 max ⁡ x ∈ 1... n max ⁡ i ∈ s o n [ x ] d [ x ] + d [ i ] + l e n g t h [ x ] [ i ] \max_{x∈1...n}\max_{i∈son[x]}d[x]+d[i]+length[x][i] maxx1...nmaxison[x]d[x]+d[i]+length[x][i]

void dp(int x) {
   
    v[x] = 1 ;
    for (int i = head[x]; i; i = e[i].nxt) {
   
        int y = e[i].to ;
        if (v[y]) continue ;
        dp(y) ;
        ans = max(ans, d[x] + d[y] + e[i].w) ;
        d[x] = max(d[x], d[y] + e[i].w) ;
    }
}

这个问题也可以通过两遍 b f s bfs bfs解决,作者在此就不多说了

②树的重心

树的重心的定义是这样的:若把树变成该点为根的有根树时,最大子树的节点数最小

这个问题也比较简单

随便选一个点作为根

s z [ x ] sz[x] sz[x]表示以 x x x为根的子树大小

不难发现 s z [ x ] = ∑ y ∈ s o n [ x ] s z [ y ] + 1 sz[x]=\sum_{y∈son[x]}sz[y]+1 sz[x]=yson[x]sz[y]+1

删除 x x x节点后最大连通块有多少节点? 子树最大有 m a x { s z [ y ] } max\{sz[y]\} max{ sz[y]}个,还有上面的它的祖先构成的树大小为 n − s z [ x ] n-sz[x] nsz[x] d p dp dp时顺便更新答案

void dfs(int rt, int fat) {
   
	sz[rt] = 1 ;
	rep(i, 0, siz(e[rt]) - 1) {
   
		int to = e[rt][i] ;
		if (to == fat) continue ;
		dfs(to, rt) ;
		sz[rt] += sz[to] ;
		dp[rt]= max(dp[rt], dp[to]) ;
	}
	dp[rt] = max(dp[rt]<
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值