前言:树形动态规划是比较套路的动态规划类型,其中有一些规律性的东西值得总结,下文树形动态规划将用树形dp代替。
树形dp的套路(一般都是成立):
1.以结点从深到浅的顺序作为dp的“阶段".(枚举顺序)
2.第一维常常是结点的编号,状态的定义常常是"以i的子树......"或者”i的.......(全部)".
3.状态一般是有子节点传递过来.(状态转移方程)
通用结构(c++):
树形dp框架
typedef struct node{
int from,to,dist;
node(int a,int b,int c):from(a),to(b),dist(c){}
}node;
void dfs(int step){//结点的编号
v[step]=true;//记录结点已经被访问过了,避免绕圈
dp[step]=......//初始化
bool flag=false;//判断叶子结点
for(int i=0;i<(int)g[step].size();i++){//枚举子节点
node y=edge[g[step][i]]; //邻接表存边
if(v[y.to]) continue;//如果访问过就退出
dfs(y.to);
flag=true;
dp[step]=min(.......)//状态转移方程处
}
if(!flag){
dp[step]=....//叶结点单独初始化
}
return;
}
如何想到树形dp?:1.该结构是一颗树(边数==结点数-1)。
2.具备由叶子节点可以递推到根的性质(无后效性)。
解决树形dp的方法:1.按一般树形dp处理,十分常规的题型。
2.当遇到要枚举根节点或者状态可以由父亲更新,用换根法,两遍dfs。(列)
3.遇到依赖性背包问题(树形背包问题),结合分组背包思想求解。(列)
依赖性背包问题(树形dp):
1.遇到拿某样物体必须要拿某样东西就是依赖性背包问题了。
2.核心思想:常规树形dp+分组背包思想
3.分组背包思想:设为前i组拿了j个物体的最大价值,为拿了j个物体最大价值(滚动 数组优化)
for(int i=1;i<=n;i++){//阶段
for(int j=0;j<=m;j++){//状态
for(int k=1;k<=l;k++){//枚举决策 这三者枚举不得更换
if(j>=w[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i][l]]+v[i][l]);
}
}
}
滚动数组优化后
for(int i=1;i<=n;i++){//阶段
for(int j=m;j>=0;j--){//状态
for(int k=1;k<=l;k++){//枚举决策 这三者枚举不得更换
if(j>=w[i]) dp[j]=max(dp[j],dp[j-w[i][l]]+v[i][l]);
}
}
}
4.依赖型背包问题核心代码展示:
void dfs(int step){
for(int i=0;i<(int)g[step].size();i++){//枚举物体
int y=g[step][i];
dfs(y);
for(int j=m;j>=0;j--){//枚举空间
for(int k=0;k<=m-1;k++){//决策
if(j>=k) dp[step][j]=max(dp[step][j],dp[step][j-k]+dp[y][k]);
}
}
}
for(int i=m;i>=1;i--){
dp[step][i]=dp[step][i-1]+num[step];//把老大搞进去
//注意这里不能写成dp[step][i]+=num[step]; 含义不同
}
}
换根法:
一般用于求解要枚举根的树形dp或者状态更新包含由父亲结点更新的树形dp
方法:1.任选一个结点为根,进行树形dp,状态更新自底向上。
2.从第一次结点出发,进行树形dp,状态更新自上向下。
核心代码展示:
//第一次更新 自底向上
void dfs_d(int step){
book[step]=1;
for(int i=0;i<(int)g[step].size();i++){
node y=edge[g[step][i]];
if(book[y.to]) continue;
dfs_d(y.to);// 注意递归顺序不同
if(deg[y.to]==1) dp[step]+=y.dist;//判断边界条件
else dp[step]+=min(dp[y.to],y.dist);
}
}
//第二次更新 自上向底
void dfs_f(int step){
book[step]=1;
for(int i=0;i<(int)g[step].size();i++){
node y=edge[g[step][i]];
if(book[y.to]) continue;
if(deg[step]==1){//判断边界条件
f[y.to]=dp[y.to]+y.dist;
}else f[y.to]=dp[y.to]+min(f[step]-min(dp[y.to],y.dist),y.dist);
dfs_f(y.to); //注意递归顺序不同
}
}
经典例题:
1.普通dp:没有上司的舞会,trategic game, Cell Phone Network
2.需要转化对象的树形dp:二叉苹果树
3.依赖性背包问题:选课,有线电视网
4.换根法:点权(牛客练习赛93c题),Accumulation Degree.,computer