加载中...

树上带依赖的01背包

大致意思就是:一共 \(n\) 件物品,每件物品有 \(0\)\(1\) 件依赖的物品,只有这件物品依赖的物品被选后才能选这件物品。每件物品都有体积和价值,求对于体积为 \(V\) 的背包,可获得的最大总价值。

先假设不存在互相依赖的关系。由于每件物品最多只会依赖一件物品,构成的 DAG 一定由若干棵树组成,其中无依赖的物品会作为树的根。

由于有多棵树,我们不难想到用一个超级源点将所有树的根连起来,进而将多棵树合并成一棵树。于是我们可以只对一棵树进行 dp。

loj160

和上述一样,模板题,并且必须要求 \(O(nm)\) 的复杂度。

code

P2014

问题与上述相同:每个结点代表每门课程,选课数量代表体积,学分代表价值。

先写一个比较暴力,普通的 01 背包解法:code1

复杂度 \(O(nm^{2})\)

然后记录一种 \(O(nm)\) 的解法,需要利用 dfn 序以及一种巧妙的状态定义。

我们考虑每次选取在 dfn 序上的某个后缀的所有物品。

状态定义

\(dp_{i,j}\):考虑 dfn 序取值在 \([i,n]\) 内的所有结点代表的物品,选择的所有物品一定保持从上到下连续的子结构(不能有断层);背包体积为 \(j\),最大总价值。

状态转移

决策 \(dfn[u]=i\) 的结点 \(u\) 是否选择。

  • 不选结点 \(u\):显然子树 \(u\) 内的所有结点都不能选择,因为连根结点 \(u\) 都没选,子树里的每个点肯定都选不了。于是,我们只需要考虑 dfn 在 \([i + siz[u], n]\) 内的所有结点(利用了子树的 dfn 连续的特点)。由于它是 \([i, n]\) 的一个子后缀,因此这个问题便是之前已经求解过的子问题,可以直接转移过来。
  • 选结点 \(u\):由于选择了结点 \(u\),因此子树 \(u\) 内的每个结点可以作考虑。可以直接继承 dfn 在 \([i + 1, n]\) 时的答案。

转移的核心在于:始终保持从上到下选择结点连续的子结构,使得每次转移均合法。

于是在求解某个子树 \(u\)\(dp_{u,j}\) 时,只需要枚举一层 \(j\)。具体实现见 code。

注意一共有 \(n + 2\) 个结点,其中多了一个超级源点 \(0\) 和一个空结点 \(n + 1\)(因为 \(dfn[u] + siz[u]\) 的最大取值可以达到 \(n + 1\),防止数组越界),这两个结点均不代表任何物品,体积和价值均为 \(0\)

code2

posted @ 2026-02-25 11:01  小橘奏  阅读(17)  评论(0)    收藏  举报