Files
python/TangDou/AcWing/BeiBao/10_SanWei.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

75 lines
3.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = N << 1;
int v[N], w[N];
int n, m, root;
// f[u][i][j] 以u为根,在前i个子树中(组)选择,最大体积不超过j的所有方案
// 属性:max价值
int f[N][N][N];
// 链式前向星
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// u:以u为根的树
// 返回值:以u为根的树,一级子树son的个数
int dfs(int u) {
/*
① 以u为根的树,如果现在剩余体积j>=v[u], 最起码可以把u装下,能获取到w[u]的价值
如果你想研究一下以u为根的子树最多可以获得多大价值,那u节点是必须装下来,否则后续子树根本没机会讨论
*/
for (int j = v[u]; j <= m; j++) f[u][0][j] = w[u];
// ② 考虑u的每个子树
int s = 0;
for (int i = h[u]; ~i; i = ne[i]) { // 枚举u的每个子节点
s++; // 前s个子树
int son = e[i]; // 在链式前向星中,这是几号节点
int c = dfs(
son); // 对子节点son,把它的f[son][c][k]信息填充完整,返回son子树的一级结点个数
/*
目标:填充f[u][i][j],
i:
1~dfs(u)的每个值,dfs(u)返回的是u的下级节点个数,也就是一级子树个数,0上面讨论过了,不用再讨论
j: 枚举每一个u子树的合法空间j
k:给定j这么大的空间,那么为son这棵子树留多大空间,k∈
[0,j-v[u]],son子树可以贡献的最大价值依定义就是f[son][c][k] f[u][s -
1][j -
k]:在处理s个子树前,前s-1个都已经填充完毕,可以依赖。由于son占了k个空间,那么前序依赖就是f[u][s-1][j-k]
*/
for (int j = v[u]; j <= m; j++)
for (int k = 0; k <= j - v[u]; k++)
f[u][s][j] = max(f[u][s][j], f[u][s - 1][j - k] + f[son][c][k]);
}
return s; // 返回u有多少个子结点
}
int main() {
// 初始化链式前向星
memset(h, -1, sizeof h);
cin >> n >> m; // 物品个数和背包容量
for (int i = 1; i <= n; i++) { // n个物品
int p;
cin >> v[i] >> w[i] >> p; // 体积、价值、父亲
if (p == -1)
root = i; // 对一棵树而言,根是最重要的
else
add(p, i); // 从父亲向儿子建边,一般树都是从上到下建边
}
int s = dfs(root); // 从根节点开始遍历,填充DP Table,返回值是root有几个子树
/*
Q:为什么需要dfs返回root有多少个子树呢?
A:DP问题,一般的答案都在状态转移方程上。比如f[u][i][j]表示以u为根的树中,在前i个儿子中去找,在体积不超过j的情况下,最大的价值是多少。
我们可以看出,最终的答案就是f[root][root的儿子总数][m]
这样看来,计算返回出root的儿子总数就是合情合理了
*/
printf("%d\n", f[root][s][m]);
return 0;
}