树上背包

树上背包

树上背包的模板 P2014 [CTSC1997] 选课

有 \(n\) 个节点,背包大小 \(m\) ,求最大价值。

树上背包是有依赖的背包。只有选择了父亲,才能选择儿子。既然是树上的,自然设出 \(f_{i,j}\) 表示在以 \(i\) 为根节点的子树内选择了大小为 \(j\) 的课,最大价值为 \(f\) 。

Code:

void dfs(int x){
    f[x][1]=c[x];
    for(auto i:v[x]){
        dfs(i);
        for(int j=m;j>=1;j--){
            for(int k=0;k<=j-1;k++){
                f[x][j]=max(f[x][j],f[x][j-k]+f[i][k]);
            }
        }
    }
}

每个节点都会作为儿子访问一遍。为了合并已经处理的 \(x\) 和当前的子树 \(i\) ,时间是 \(O(m^2)\) 。总共是 \(O(nm^2)\) 。有更快的解法。

可以计算 \(size\) 。进入子树后,限定循环的范围,时间复杂度可以到 \(O(nm)\) 。但是这个方法适用性不好,改一改每个节点的物品大小就不行了。

有更加巧妙的做法。利用 dfs序,将树上的变为线性的结构。要按 dfs序倒序枚举,才能先枚举儿子在枚举父亲,符合要求。此时设 \(f_{i,j}\) 表示在 dfs序为 \(i \sim n\) 中选择 \(j\) 的最大价值。我们对每个节点讨论:

  1. 如果不选当前节点。那么它的子树内都不能选了。\(f_{i,j}=f_{i+siz_i,j}\)
  2. 如果选,就像0/1背包那样选取。\(f_{i,j}=f_{i+1,j-1}+c_i\)

Code:

int cnt,dfn[N],fid[N];
int siz[N];
int f[N][N];
void dfs(int x){
    dfn[x]=++cnt;
    fid[cnt]=x;
    siz[x]++;
    for(auto i:v[x]){
        dfs(i);
        siz[x]+=siz[i]; 
    }
}
for(int i=n+1;i>=1;i--){
    for(int j=1;j<=m;j++){
        int x=fid[i];
        f[i][j]=max(f[i+siz[x]][j],f[i+1][j-1]+c[x]);
    }
}

此时就将树形依赖的背包转化为由 dfs序下标并限定选取的0/1背包。这样做避免了两个子树信息的合并,转而求之前信息与当前节点的合并,时间复杂度优化到了 \(O(nm)\) 。

例1:P2515 [HAOI2010] 软件安装

问题:现在我们的手头有 \(N\) 个软件,对于一个软件 \(i\),它要占用 \(W_i\) 的磁盘空间,它的价值为 \(V_i\)。我们希望从中选择一些软件安装到一台磁盘容量为 \(M\) 计算机上,使得这些软件的价值尽可能大(即 \(V_i\) 的和最大)。

但是现在有个问题:软件之间存在依赖关系,即软件 \(i\) 只有在安装了软件 \(j\)(包括软件 \(j\) 的直接或间接依赖)的情况下才能正确工作(软件 \(i\) 依赖软件 \(j\))。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为 \(0\)。

我们现在知道了软件之间的依赖关系:软件 \(i\) 依赖软件 \(D_i\)。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则 \(D_i=0\),这时只要这个软件安装了,它就能正常工作。

第 1 行:\(N,M(0\leq N\leq 100, 0\leq M\leq 500)\)

第 2 行:\(W_1,W_2, ... W_i, ..., W_n (0\leq W_i\leq M)\)

第 3 行:\(V_1, V_2, ..., V_i, ..., V_n (0\leq V_i\leq 1000)\)

第 4 行:\(D_1, D_2, ..., D_i, ..., D_n (0\leq D_i\leq N, D_i≠i)\)

缩点+树型dp

分析:\(n\) 条边,是基环树森林。可能构成环。要先缩点,因为只有 \(n\) 条边,将缩点后所有入度为0的点与0号点相连后会构成一棵树。做树上背包。

for(int i=cnt1;i>=1;i--){
    for(int j=m;j>=0;j--){
        int x=idn[i];
        f[i][j]=max(f[i][j],f[i+siz[x]][j]);
        if(j>=w1[x])f[i][j]=max(f[i][j],f[i+1][j-w1[x]]+c1[x]);
    }
}
posted @ 2026-09-27 17:30  TanHaoren  阅读(7)  评论(0)    收藏  举报