题解:
常规滚动数组优化的分组背包:
1. 枚举分组
2. 倒序枚举体积
3. 枚举该组每个物品
本题:选择子树必须先选择根
考虑一:先不考虑根
根物品体积为v[root],价值为w[root],因此子树的枚举最大体积为 m - v[root]
体积:j = [0, m - v[u]]
该子树的物品:k = [0, j]
最后更新f[root],体积:j = [v[root], m]
考虑二:先考虑根
初始化f[root][v[root], m] = w[root]
体积:j = [v[root], m] 至少存下根
该子树的物品:k = [0, j - v[root]]
整体来看考虑二更符合直觉,同时不需要在枚举完后再更新f[root][0, v[root]] = 0,因为其未被更新
代码:
解法一:
#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 110;
int f[N][M];
int n, m, v[N], w[N];
int root, p;
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
//枚举背包
for(int i = h[u]; ~i; i = ne[i]) {
int son = e[i];
dfs(son);
for(int j = m - v[u]; j >= 0; --j)
for(int k = 0; k <= j; ++k)
f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
}
for(int j = m; j >= v[u]; --j) f[u][j] = f[u][j - v[u]] + w[u];
for(int j = 0; j < v[u]; ++j) f[u][j] = 0;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, n + 1 << 2); idx = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d%d%d", &v[i], &w[i], &p);
if(p == -1) root = i;
else add(p, i);
}
dfs(root);
printf("%d\n", f[root][m]);
return 0;
}
解法二:
#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 110;
int f[N][M];
int n, m, v[N], w[N];
int root, p;
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
for(int i = v[u]; i <= m; ++i) f[u][i] = w[u];
for(int i = h[u]; ~i; i = ne[i]) {
int son = e[i];
dfs(son);
for(int j = m; j >= v[u]; --j)
for(int k = 0; k <= j - v[u]; ++k)
f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, n + 1 << 2); idx = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d%d%d", &v[i], &w[i], &p);
if(p == -1) root = i;
else add(p, i);
}
dfs(root);
printf("%d\n", f[root][m]);
return 0;
}
我看不懂,但我大为震撼
tql