20251109 树状DP总结
引子
树状DP,就是在树上进行动态规划,由于树的特殊递归属性,所以树状DP都是在树上进行的。
树状DP可以分成两种:
- 兄弟节点间无相互约束
- 兄弟节点间有相互约束
第一种,兄弟节点间无相互约束的树状DP,这种树状DP意味着兄弟节点之间互不干扰,你干你的,他干他的。这种树状DP的经典例题有 洛谷 没有上司的舞会。
第二种,兄弟节点间有相互约束的树状DP,这种树状DP意味着兄弟节点间会互相干扰,就像给这几个兄弟节点分苹果,你分得多一点别人就分的少一点,你分得少一点别人就分的多一点。这种树状DP的经典例题有 洛谷 二叉苹果树。
P1352 没有上司的舞会
题目传送门
简化题意:有 n 个点,他们的从属关系用一颗树来表示,父结点是子结点的直接上司。每个点有一个点权,求拿出若干个点且这若干个点的任意两点无从属关系的最大点权和。
我们定义状态 dp[i][0/1]dp[i][0/1]dp[i][0/1] 表示以节点𝑖为根的子树的最大点权值。其中:
- 第二维取0表示节点 i 不参加舞会
- 第二维取1表示节点 i 参加舞会
状态转移方程如下(设 v 为 x 的子节点):
- 当 x 不参加舞会时:
dp[x][0]+=max(dp[v][1],dp[v][0]);dp[x][0] += max(dp[v][1],dp[v][0]);dp[x][0]+=max(dp[v][1],dp[v][0]);
即子节点可以自由选择是否参加 - 当 x 参加舞会时:
dp[x][1]+=dp[v][0];dp[x][1] += dp[v][0];dp[x][1]+=dp[v][0];
此时所有子节点都不能参加
我们用DFS遍历这棵树,在回溯时更新每个节点的DP。
#include<bits/stdc++.h>
using namespace std;
int r[6005],dp[6005][2];
vector<int> E[6005];
void dfs(int x,int fa){dp[x][1]=r[x];for(int i=0;i<E[x].size();i++){int v=E[x][i];if(v==fa)continue;dfs(v,x);dp[x][0]+=max(dp[v][1],dp[v][0]);dp[x][1]+=dp[v][0];}
}
int main(){int n;cin>>n;for(int i=1;i<=n;i++){cin>>r[i];}for(int i=1;i<n;i++){int u,v;cin>>u>>v;E[u].push_back(v);E[v].push_back(u);}dfs(1,0);cout<<max(dp[1][0],dp[1][1]);return 0;
}
P2015 二叉苹果树
题目传送门
题意:给定一棵树,n个点,有边权,至少保留q条边,求最大边权和。
1.q条边分到左子树多,那么右子树就少,兄弟节点间有数量约束,考虑第二类树型DP。
2.状态:dp[i][j] 表示以 i 为根节点的子树保留至多 j 条边的最大边权和。
3.答案: dp[1][q]
4.状态转移:
int v=E[x][i].v,w=E[x][i].w;
if(v==fa)continue;
dfs(v,x);
sz[x]+=sz[v];
for(int j=min(q,sz[x]);j>=0;j--){for(int k=0;k<=min(j-1,sz[v]);k++){dp[x][j]=max(dp[x][j],dp[v][k]+dp[x][j-k-1]+w);}
}
5.初始状态:dp[x][0]=0dp[x][0]=0dp[x][0]=0
#include<bits/stdc++.h>
using namespace std;
struct node{int u,v,w;
};
vector<node> E[105];
int n,q,sz[105],dp[105][105];
void dfs(int x,int fa){sz[x]=1;for(int i=0;i<E[x].size();i++){int v=E[x][i].v,w=E[x][i].w;if(v==fa)continue;dfs(v,x);sz[x]+=sz[v];for(int j=min(q,sz[x]);j>=0;j--){for(int k=0;k<=min(j-1,sz[v]);k++){dp[x][j]=max(dp[x][j],dp[v][k]+dp[x][j-k-1]+w);}}}
}
int main(){cin>>n>>q;for(int i=1;i<n;i++){int u,v,w;cin>>u>>v>>w;E[u].push_back({u,v,w});E[v].push_back({v,u,w});}dfs(1,0);cout<<dp[1][q];return 0;
}
