树形dp算法

一 定义

 树形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 进行存储,因为上下级关系式一个单向的关系,所以我们只需要去存储一个方向上的关系。

  1. 存储关系
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;
}
  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];
  }
}

这道题可以被称作经典。

树上染色 树形dp进阶

这次的节点的数目比较多,所以使用前链向式星进行存图

在这道题目里面我们要得到的是如果安排这些黑色的节点使得分数之和最大,得到的状态转移方程如下,在这种情况下要使用

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][jk]+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);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值