python背包问题满减_AcWing 10. 有依赖的背包问题(python with detail)

'''

树形dp转分组背包问题,每个节点都会有一个f[j],转换为分组背包从叶子节点向上运算

dp[u,i,j] 代表选了以u为根的子树, 只考虑其前i个子树时, 背包容量为j时, 可获得的最大价值 -> f[u][j]

'''

import sys

import math

########################处理输入##############################

x = sys.stdin.readlines()

n, m = map(int, x.pop(0).rstrip().split()) #物品数量、背包体积

y = [ [] for i in range(len(x))]

for i in range(len(x)):

y[i] = [int(j) for j in x[i].rstrip().split()]

f = [[0 for j in range(m+1)] for i in range(n+1)]

#########################代码主体##############################

h = [-1 for _ in range(n+1)]

e = [0 for _ in range(n+1)]

ne = [0 for _ in range(n+1)]

v = [0 for _ in range(n+1)]

w = [0 for _ in range(n+1)]

idx = 1 #边的序号

def addedge(a, b): #建立链式前向星,a起点指向b终点

if a >= 0 and b >= 0: #起点、终点序号都得合法; 防止-1这种列表序号

global idx

e[idx] = b #终点

ne[idx] = h[a] #同起点下一条边,构成链表效果

h[a] = idx #h的长度代表不同起点的数量, "当前边"就是以点a为起点的最后一条边

idx += 1

def dfs(u): #u代表子树的根节点,利用链式前向星进行dfs,起点代表树的父节点而终点代表子节点,叶节点指向-1

global f

i = h[u] #边的序号

while i != -1:

son = e[i] #子节点

dfs(son)

for j in range(m-v[u], -1, -1): #体积从大到小, 必拿本节点

for k in range(1, j+1): #分组背包问题,因为同个子树容量不同时,决策不同,决策之间互斥可以看作不同的物品

f[u][j] = max(f[u][j], f[u][j-k] + f[son][k]) #从子树更新最大价值情况

i = ne[i]

for i in range(m, v[u]-1, -1): #背包容量足够就加上当前节点

f[u][i] = f[u][i-v[u]] + w[u]

for i in range(0, v[u]): #背包容量不够就不加当前节点,但是也不能放任何子节点

f[u][i] = 0

def solution(n, m, items):

# dp[u,i,j] 代表选了以u为根的子树, 只考虑其前i个子树时, 背包容量为j时, 可获得的最大价值 -> f[u][j]

#每算某个节点,先算它的子节点

root = 0

for i in range(1, len(items)+1): # i代表节点序号

v[i], w[i], p = items[i-1]

if p == -1: root = i

else: addedge(p, i) #else可加可不加

dfs(root)

return f[root][m]

######################################################################################

print(solution(n, m, y))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值