树上DP笔记

树是一个由 n 个节点,n1 条边组成的联通图,图上没有环,其每一条边都是割边。

在树上设计动态规划算法时,一般以节点从深到浅的顺序作为 DP 的阶段。大多数时候,采用递归的方式实现树形动态规划。

对于每一个节点 x,先对它的每一个子节点进行 DP,回溯时从子节点向 x 进行状态转移。

例题 1 P1352

以节点编号作为 DP 状态的第 1 维,一名职员是否愿意参加,只与他的直接上司是否参加有关。我们定义 f[x,0] 表示从以 x 为根的子树中邀请一部分职员参会,且 x 不参加舞会时,快乐总和的最大值,f[x,1] 则代表 x 参加舞会时快乐总和的最大值。

那么状态转移方程为:

f[x,0]=sson(x)max(f[s,0],f[s,1]);

f[x,1]=h[x]+sson(x)f[s,0];

如果是叶子结点,那么:

f[x,0]=0,f[x,1]=h[x]

设根节点为 root,那么答案为 max(f[root,0],f[root.1])

#include<bits/stdc++.h>
using namespace std;
int n,r[100009],f[100009][2];
vector<int> to[100009];
void dp(int x,int fa){
for(int i=0;i<to[x].size();i++){
if(to[x][i]==fa) continue;
dp(to[x][i],x);
}
for(int i=0;i<to[x].size();i++){
f[x][0]+=max(f[to[x][i]][0],f[to[x][i]][1]);
f[x][1]+=f[to[x][i]][0];
}
f[x][1]+=r[x];
return ;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>r[i];
for(int i=1;i<=n-1;i++){
int x,y;
cin>>x>>y;
to[x].push_back(y);
to[y].push_back(x);
}
dp(1,0);
cout<<max(f[1][0],f[1][1]);
return 0;
}

例题 2 P2014

我们定义 f[x,t] 为以 x 为根的子树中选修 t 门课能获得的最大学分。son(x)x 的子节点集合,个数 p=|son(x)|

如果 x 学习完毕,则可以学习 x 的子节点,对于每个子节点 yison(x),在以 yi 为根的子树中选择 Ci 门课,则在满足 yson(x)=t1 的情况下获得尽可能多的学分。

t=0 时,f[x,t]=0

t>0 时,状态转移方程如下:

f[x,y]=maxi=1pci=t1{i=1pf[yi,ci]}+score[x]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,s[1009],f[1009][1009];
vector<int> to[1009];
void dp(int x){
f[x][0]=0;
for(int i=0;i<to[x].size();i++){
int y=to[x][i];
dp(y);
for(int t=m;t>=0;t--){
for(int j=0;j<=t;j++) f[x][t]=max(f[x][t],f[x][t-j]+f[y][j]);
}
}
if(x!=0){
for(int t=m;t>0;t--) f[x][t]=f[x][t-1]+s[x];
}
return ;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int ki,si;
cin>>ki>>si;
to[ki].push_back(i);
s[i]=si;
}
dp(0);
cout<<f[0][m];
return 0;
}

例题 3 P4395

题目和气垫车有什么关系

看到题目的第一反应可能是贪心 12 染色,但可以被菊花图 hack。

进过一系列玄学计算可以算出取值上界大约是 logn+1,在本题中是 15,保险一点取 20。令 f[x,t] 表示节点 xt 时的最小权值和。

如果 x 为叶节点,f[x,t]=t

否则

f[x,t]=sson(x)minkt{f[s,k]}+t

答案为 min{f[1,t]}

#include<bits/stdc++.h>
using namespace std;
int n,f[10009][29];
vector<int> to[10009];
void dp(int x,int fa){
for(int i=1;i<=20;i++) f[x][i]=i;
for(int i=0;i<to[x].size();i++){
if(to[x][i]==fa) continue;
dp(to[x][i],x);
for(int j=1;j<=20;j++){
int minn=0x3f3f3f3f;
for(int k=1;k<=20;k++){
if(j==k) continue;
minn=min(minn,f[to[x][i]][k]);
}
f[x][j]+=minn;
}
}
return ;
}
int main(){
cin>>n;
for(int i=1;i<=n-1;i++){
int x,y;
cin>>x>>y;
to[x].push_back(y);
to[y].push_back(x);
}
dp(1,0);
int ans=0x3f3f3f3f;
for(int i=1;i<=20;i++) ans=min(ans,f[1][i]);
cout<<ans;
return 0;
}
posted @   11jiang08  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示