动态规划(六)——树形dp
树形dp,又称树状dp,即在树上进行的dp,在设计动态规划算法时,一般就以节点从深到浅(子树从小到大)的顺序作为dp的“阶段”,dp的状态表示中,第一维通常是节点编号(代表以该节点为根的子树)。大多数时候,我们采用递归的方式实现树形动态规划。对于每个节点x,先递归在他的每个子节点上进行dp,在回溯时从子节点向节点x,进行状态转移。
例题:
没有上司的舞会(Acwing 285)
题目描述 Ural 大学有 N 个职员,编号为 1~N。他们有从属关系,也就是说他们的关系就像一棵以校长为根的树, 父结点就是子结点的直接上司。每个职员有一个快乐指数。现在有个周年庆宴会,要求与会职员的快乐 指数最大。但是,没有职员愿和直接上司一起与会。 输入格式 第一行一个整数 N。(1<=N<=6000) 接下来 N 行,第 i+1 行表示 i 号职员的快乐指数 Ri (-128<=Ri<=127) 接下来 N-1 行,每行输入一对整数 L,K。表示 K 是 L 的直接上司。 最后一行输入 0,0。 输出格式 输出最大的快乐指数。 样例 样例输入 7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5 0 0 样例输出 5
思路:
所有点形成一个森林,设f[v][0]表示v为根的子树如果v 不参加舞会,能得到的最大快乐指数,f[v][1]表示v为根的子树如果v参加舞会,能得到的最大快乐指数。那么在计算f[v]的时候,可以先递归计算其所有孩子的f值,然后考虑f[v],对于f[v][0],由于v没参加,所以其子树树根可以参加也可以不参加,所以
f [v] [0]=∑ v → u max {f [u][0],f[u][1]}
对于f[v][1],由于v参加了,所以其所有子树树根都不能参加,所以
f [v][1]=∑ v → u f[u][0]
最后对于所有树根vi ,求一下∑ i max {f[vi][0],f [vi] [1]} 即为答案。
#include <bits/stdc++.h> #define N 10010 using namespace std; vector<int> son[N]; int f[N][2],v[N],h[N],n; void dp(int x){ f[x][0]=0; f[x][1]=h[x]; for(int i=0;i<son[x].size();i++){ int y=son[x][i]; dp(y); f[x][0]+=max(f[y][0],f[y][1]); f[x][1]+=f[y][0]; } } int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>h[i]; for(int i=1;i<n;i++){ int x,y; cin>>x>>y; v[x]=1; son[y].push_back(x); } int root; for(int i=1;i<=n;i++) if(!v[i]){ root=i; break; } dp(root); cout<<max(f[root][0],f[root][1])<<endl; }
#一名爱打篮球的oier#
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探