引子
树状DP,就是在树上进行动态规划,由于树的特殊递归属性,所以树状DP都是在树上进行的。
树状DP可以分成两种:
- 兄弟节点间无相互约束
- 兄弟节点间有相互约束
第一种,兄弟节点间无相互约束的树状DP,这种树状DP意味着兄弟节点之间互不干扰,你干你的,他干他的。这种树状DP的经典例题有 洛谷 没有上司的舞会。
第二种,兄弟节点间有相互约束的树状DP,这种树状DP意味着兄弟节点间会互相干扰,就像给这几个兄弟节点分苹果,你分得多一点别人就分的少一点,你分得少一点别人就分的多一点。这种树状DP的经典例题有 洛谷 二叉苹果树。
P1352 没有上司的舞会
简化题意:有 n 个点,他们的从属关系用一颗树来表示,父结点是子结点的直接上司。每个点有一个点权,求拿出若干个点且这若干个点的任意两点无从属关系的最大点权和。
我们定义状态 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1] 表示以节点𝑖为根的子树的最大点权值。其中:
- 第二维取0表示节点 i 不参加舞会
- 第二维取1表示节点 i 参加舞会
状态转移方程如下(设 v 为 x 的子节点):
- 当 x 不参加舞会时:
d p [ x ] [ 0 ] + = m a x ( d p [ v ] [ 1 ] , d p [ v ] [ 0 ] ) ; dp[x][0] += max(dp[v][1],dp[v][0]); dp[x][0]+=max(dp[v][1],dp[v][0]);
即子节点可以自由选择是否参加 - 当 x 参加舞会时:
d p [ x ] [ 1 ] + = d p [ 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.初始状态: d p [ x ] [ 0 ] = 0 dp[x][0]=0 dp[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;
}
699

被折叠的 条评论
为什么被折叠?



