树形dp学习笔记
1.算法描述
树形DP,即在树上进行的DP。(摘自OI Wiki树形 DP 部分)
2.算法解析
首先,在正式开始dp(推f数组)之前,我们要先根据给出的条件建好树,并预处理出各个之间节点的关系,有时还要预处理出各个节点size(子树大小)等信息(另外,如果题中描述的一部分关系可能会形成森林,也许还要建虚根)
树形dp常用记忆化搜索来实现,从树的根节点开始,并通过子树中的节点的f数组进行状态转移,设置的dp状态也往往与节点的子树挂钩
例题
1.Luogu P1352 没有上司的舞会
参考文献:李煜东写的《算法竞赛进阶指南》一书
设 $dp_{i,0/1}
#include<bits/stdc++.h>
using namespace std;
int n,r[6005],f[6005][2],l,k,s,fa[6005];
vector <int> son[6005];
int dfs(int x,int typ){
if(!son[x].size()){
if(typ)
return f[x][1]=r[x];
return f[x][0];
}
int g=son[x].size();
if(typ){
f[x][1]+=r[x];
for(int i=0;i<g;++i)
f[x][1]+=dfs(son[x][i],0);
return f[x][1];
}
for(int i=0;i<g;++i)
f[x][0]+=max(dfs(son[x][i],1),dfs(son[x][i],0));
return f[x][0];
}
int main(){
cin>>n;
for(int i=1;i<=n;++i)
cin>>r[i];
for(int i=1;i<n;++i){
cin>>l>>k;
son[k].push_back(l);
fa[l]=k;
}
for(int i=1;i<=n;i++){
if(!fa[i]){
s=i;
break;
}
}
cout<<max(dfs(s,0),dfs(s,1))<<endl;
return 0;
}
2.Luogu P2015 二叉苹果树
参考文献(“子谦。”的一篇题解)
设
#include<bits/stdc++.h>
#define F(i,j,k) for(int i=j;i<=k;++i)
using namespace std;
int n,q,f[105][105],head[105],fa[105],size[105],ans,tmp,a,b,c;
bool vis[101];
struct edge{
int u,v,w,next;
}s[202];
void addedge(int u,int v,int w){
++tmp;s[tmp].u=u,s[tmp].v=v,s[tmp].w=w,s[tmp].next=head[u];head[u]=tmp;
}
void dfs(int x){
if(vis[x])
return;
for(int i=head[x];i;i=s[i].next){
if(fa[x]!=s[i].v){
fa[s[i].v]=x;
dfs(s[i].v);
size[x]=size[x]+size[s[i].v]+1;
int y=s[i].v;
for(int j=min(q,size[x]);j;--j){
int r=min(q,size[y]);
for(int k=r;k>=0;--k){//循环是先能加法就尽量不先做减法(如:能枚举剩下多少就不枚举删除多少)
if(j-k-1>=0)
f[x][j]=max(f[x][j],f[y][k]+f[x][j-k-1]+s[i].w);
}
}
}
}
vis[x]=true;
}
int main(){
cin>>n>>q;
--n;
F(i,1,n){
cin>>a>>b>>c;
addedge(a,b,c);
addedge(b,a,c);//由于题目没有规定是先输入父亲节点的编号还是先输入儿子节点的编号,所以这里要先建一条无向边,再在dfs中确认父子关系
}
++n;
dfs(1);
//for(int i=1;i<=n;i++)
// ans=max(ans,f[i][q]); //注意:在树中,如果没有祖先就不会有后代了
printf("%d\n",f[1][q]);
return 0;
}
3.Luogu P2014 [CTSC1997] 选课
参考文献1(“♞老姚♘”的一篇博客)
参考文献2:李煜东写的《算法竞赛进阶指南》一书
我此题与二叉苹果树这题的思路差不多 (特别说明:我建了一个虚根,其编号为0)
#include<bits/stdc++.h>
using namespace std;
int n,m,s[305],k[305],f[305][305],siz[305];
vector <int> son[305];
void dfs(int x){
if(!son[x].size()){
siz[x]=1;
return;
}
int pp=son[x].size();
for(int i=0;i<pp;++i){
dfs(son[x][i]);
siz[x]+=siz[son[x][i]];
}
++siz[x];
}
void dfs1(int x){
//cout<<x;
int z=son[x].size();
if(!z){
f[x][1]=s[x];
return;
}
if(x)
f[x][1]=s[x];
int w=1;
for(int i=0;i<z;++i){
int q=son[x][i];
dfs1(q);
int t=siz[q];
w+=t;
for(int j=min(w,m);j;--j)
for(int k=min(j-1,t);k;--k)
f[x][j]=max(f[x][j],f[q][k]+f[x][j-k]);
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie();cout.tie();
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>k[i]>>s[i];
son[k[i]].push_back(i);
}
dfs(0);
for(int i=1;i<=n;++i)
if(!k[i])
dfs1(i);
int z=son[0].size();
int w=0;
for(int i=0;i<z;++i){
int q=son[0][i];
int t=siz[q];
w+=t;
for(int j=min(w,m);j;--j)
for(int k=min(t,j);k;--k)
f[0][j]=max(f[0][j],f[q][k]+f[0][j-k]);
}
cout<<f[0][m];
return 0;
}
本文可能会继续更改。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下