【蓝桥杯Python】试题 算法训练 强力党逗志芃

资源限制

内存限制:256.0MB   C/C++时间限制:1.0s   Java时间限制:3.0s   Python时间限制:5.0s

问题描述

  逗志芃励志要成为强力党,所以他将身上所以的技能点都洗掉了重新学技能。现在我们可以了解到,每个技能都有一个前提技能,只有学完了前提技能才能学习当前的技能(有一个最根本的技能不需要前提技能)。学习每个技能要消耗一个技能点,然后可以获得这个技能的威力值。由于逗志芃要陪妹子,所以他希望你教他如何点技能使得威力值最大从而成为强力党。

输入格式

  第一行两个数n,m表示有n个技能和m个技能点。第二行有n个数,第i个数表示第i个技能的威力值。
  之后的n-1行,每行两个数x,y,表示y技能的前提技能是x,也就是说先学第x个技能才能学弟y个技能。

输出格式

  一个数,最大的威力值。

样例输入

3 2
1 10 20
1 2
1 3

样例输出

21

数据规模和约定

  0<n,m<=200, 技能的威力值不超过200。

这个问题是一个树形动态规划问题,因为每个技能只有一个前提技能,也就是说技能之间构成了一个树形结构。动态规划的状态可以定义为`dp[i][j]`表示在以第`i`个技能为根的子树中,使用`j`个技能点能达到的最大威力值。为了填充这个`dp`数组,我们首先需要构建技能之间的依赖关系,然后从底部开始逐级向上填充`dp`表。 参考代码如下:

# 读取输入
n, m = map(int, input().split())
powers = list(map(int, input().split()))  # 各个技能的威力值
children = [[] for _ in range(n+1)]  # 子技能列表
has_parent = set()  # 存储所有有前置技能的节点集合

# 读取技能依赖关系,构建树形结构
for _ in range(n-1):
    x, y = map(int, input().split())
    children[x].append(y)
    has_parent.add(y)

# 找到根节点,即没有前提技能的最根本技能
for i in range(1, n+1):
    if i not in has_parent:
        root = i

    # 初始化动态规划数组
dp = [[0] * (m+1) for _ in range(n+1)]


# 递归深度优先搜索 - 以`skill`为根的子树
def dfs(skill):
    # 初始化以当前技能为根的子树中的动态规划值
    # 当只选择当前技能时,可获得的威力值
    for j in range(1, m + 1):
        dp[skill][j] = powers[skill - 1]  # skill的威力值

    # 遍历当前技能(skill)的所有子技能(child)
    for child in children[skill]:
        # 递归搜索子技能的子树
        dfs(child)

        # 动态规划的状态转移部分
        # 更新当前技能(skill)在使用不同技能点数下的最大威力值
        # 从高到低更新dp[skill]数组, 以便在计算时考虑到子技能的威力值并不产生影响
        for j in range(m, 0, -1):
            # 遍历可以给子技能(child)分配的技能点数
            # k是给子技能的技能点数, (j-k)是剩余给当前技能点数
            for k in range(j):
                # 我们选择最大值更新当前技能点j可以获得的最大威力值
                # 可选的是保持当前的威力值,或者加上子技能child使用k个技能点能获得的威力值
                dp[skill][j] = max(dp[skill][j], dp[skill][j - k] + dp[child][k])
# 从根技能开始进行DFS
dfs(root)

# 输出最大的威力值
print(dp[root][m])

  • 14
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值