树形背包学习笔记 + 洛谷2014 CSTS1997 选课(树形背包模板题)

17 篇文章 0 订阅
16 篇文章 0 订阅

题目链接
题意:
给你一个森林,有 n n 个点,每个点有一个价值,能选某个点的前提是它的所有祖先节点都被选,让你求只能选m个节点的最大价值。
题解:
首先先把所有没有父节点的点向 0 0 号点连边,让森林变成一棵树,然后我们由叶子向根dp,设dp[i][j]为在根节点为 i i 的子树里选j个节点的最大获益。当 j=0 j = 0 时, dp[i][j]=0 d p [ i ] [ j ] = 0 。当 j0 j ≠ 0 时,设 yson[x] y ∈ s o n [ x ] c[1,n] c ∈ [ 1 , n ]

dp[i][j]=max(i=1son[x]dp[y][c])+val[x] d p [ i ] [ j ] = m a x ( ∑ i = 1 s o n [ x ] d p [ y ] [ c ] ) + v a l [ x ]

这可以转化为一个分组背包,每个子树看作一组,从每组中至多选一个物品,体积为 j j ,价值为dp[y][j],这样对这个树形结构进行背包dp的转移就行。
复杂度 O(n3) O ( n 3 ) ,注意 0 0 号点是个虚拟节点,实际上不需要被选,dp[0][m]就是最终答案。
洛谷2014选课代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,son[310][310],dp[310][310],val[310],cnt[310];
void dfs(int x)
{
    for(int i=1;i<=cnt[x];++i)
    {
        int y=son[x][i];
        dfs(y);
        for(int j=m;j>=0;--j)
        {
            for(int k=j;k>=0;--k)
            dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]);
        }
    }
    if(x!=0)
    {
        for(int i=m;i>=1;--i)//注意倒叙枚举 
        dp[x][i]=dp[x][i-1]+val[x];//x本身需要占一门课
    }   
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        int x;
        scanf("%d%d",&x,&val[i]);
        son[x][++cnt[x]]=i;
    }
    dfs(0);
    printf("%d\n",dp[0][m]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值