一 定义
树形dp通常指在各变元之间的关系是一个树形的结构,而非传统的线性结构,对于这种树形的dp,我们通常使用DFS去解决,时间的复杂度通常在 O ( n 2 ) O(n^2) O(n2)
二 经典例题
题目的意思大概是在一个公司里面有着上下级关系,每个人都有一个快乐指数,在一个舞会里面,如果你邀请了一个人,那么他的直接下级就不能参加,基于这个状态,请问我们如何邀请可以使得快乐指数之和最大。
解题思路
在点的数目较少的时候,我们可以使用 v e c t o r vector vector 进行存储每个点对应的子节点,点的数目多的时候我们可以使用前链向式星进行存储。
在这个题目中,点的数目较少,所以我们直接使用 v e c t o r vector vector 进行存储,因为上下级关系式一个单向的关系,所以我们只需要去存储一个方向上的关系。
- 存储关系
int n = 0;
scanf("%d",&n);
for(int i=1;i<=n;i++) cin>>h[i];
for(int i=1;i<=n-1;i++)
{
int x,y;
cin>>x>>y;
son[y].push_back(x);
v[x]=1;
}
- 进行dp
由于这个是一个树形的结构,所以我们使用 d f s dfs dfs 进行 d p dp dp,我们 d p dp dp 的结构是 d p [ k ] [ j ] dp[k][j] dp[k][j] , k k k表示的是进行 $ dp$ 的点, j ( 0 / 1 ) j(0/1) j(0/1) 表示不选择该点和选择该点。
如果一点被选择,那么注定下一个点是不能选择的
f[x][1] += f[son[x]][i][0];
如果一点没有被选择,那么应该选择下一个点是选择还是不选择
f[x][0] += max(f[son[x][i]][1],f[son[x]][i][0])
具体的 d p dp dp 内容如下
void dp(int x)
{
dp[x][0] =0;
dp[x][1] = h[x];
for(int i = 0;i < son[x].size();i++)
{
dp(son[x][i]);
f[x][0] += max(f[son[x][i]][1],f[son[x]][i][0]) ;
f[x][1] += f[son[x]][i][0];
}
}
这道题可以被称作经典。
这次的节点的数目比较多,所以使用前链向式星进行存图
在这道题目里面我们要得到的是如果安排这些黑色的节点使得分数之和最大,得到的状态转移方程如下,在这种情况下要使用
d p [ n o w ] [ j ] = max ( d p [ n o w ] [ j ] , d p [ n o w ] [ j − k ] + d p [ t o ] [ k ] + v a l u e ) dp[now][j] = \max(dp[now][j],dp[now][j-k]+dp[to][k]+value) dp[now][j]=max(dp[now][j],dp[now][j−k]+dp[to][k]+value)
主要的DFS如下
void DFS(int now,int fa){
size[now] = 1;dp[now][0] = dp[now][1] = 0;
for (int i = head[now];~i;i = E[i].next){
int to = E[i].to;
if(to == fa) continue;
DFS(to,now);
size[now] += size[to];
for (int j = min(m,size[now]);j >= 0;j--){
if (dp[now][j] != -1) dp[now][j] += dp[to][0] + (ll)size[to] * (n - m - size[to]) * E[i].cost;
for(int k = min(j,size[to]);k;k--){
if(dp[now][j-k]==-1) continue;
ll value = (ll)(k*(m-k) + (size[to] - k) * (n - m - size[to] + k))*E[i].cost;
dp[now][j] = max(dp[now][j], dp[now][j - k] + dp[to][k] + value);
}
}
}
}