'''
用树形dp的思想来求解
dp(i, j)表示在i节点为根的子树中选择节点,总开销不超过j的所有选法中,最优选法的价值的最大和
在i子树上选择,根节点是必选的,剩下的容量需要分配给其子树,把每一个
子树看成一个分组,每个分组中选择这个分组需要的容量,这些子树的总容量加起来不能超过i子树的剩下容量
这一步就转换为一个分组背包问题,物品就是选择的分配给子树的容量,价值就是这个子树在该容量约束下的最大子节点价值和
分组背包完成后,就可以得到一种最佳的剩余容量分配方案让i的所有子树的价值和最大,进而dp(i, j)也就求出来了
'''
N, V = map(int, input().split())
arr = [(0, 0)]
link = {} # 邻接表
root_id = -1
link = {i: [] for i in range(1, N + 1)}
for i in range(1, N + 1):
v, w, p = map(int, input().split())
if p == -1:
root_id = i
else:
link[p].append(i)
arr.append((v, w))
# print(link)
# i节点为根的子树中选择节点,总开销不超过j的所有选法中,最优选法的价值的最大和
from functools import lru_cache
@lru_cache(typed=False, maxsize=128000000)
def dp(i, j):
if j < arr[i][0]:
return 0
# 根节点的收益
ans = arr[i][1]
left_j = j - arr[i][0]
if left_j == 0:
return ans
# 子节点进行分组背包
if len(link[i]) == 0:
return ans
if len(link[i]) == 1:
ans += dp(link[i][0], left_j)
return ans
n = len(link[i])
f = [0] * (left_j + 1) # f(ii, jj)表示前ii棵子树总容量不超过jj的约束下,这些子树中选择节点的选法中,最优选法对应的最大的价值和
for ii in range(n):
for jj in range(left_j, -1, -1):
if ii == 0:
f[jj] = dp(link[i][ii], jj)
else:
for kk in range(1, jj + 1):
f[jj] = max(f[jj], f[jj - kk] + dp(link[i][ii], kk))
if i == 0:
print(ans, f[left_j])
ans += f[left_j]
return ans
print(dp(root_id, V))
“把每一个子树看成一个分组,每个分组中选择这个分组需要的容量,这些子树的总容量加起来不能超过i子树的剩下容量这一步就转换为一个分组背包问题,物品就是选择的分配给子树的容量。’‘
最后一句话 ”物品就是选择的分配给子树的容量“ 如何理解呢?
其实吧,前几行可以写在代码外面的