贪心+模拟 · 小E的怪物挑战(中) | 豆包MarsCode AI刷题

231 阅读5分钟

问题描述

小E在一个游戏中遇到了 nn 个按顺序出现的怪物,每个怪物都有其特定的血量 hihi​ 和攻击力 aiai​。小E的初始血量为 HH,攻击力为 AA。 游戏规则如下:

  1. 小E可以击败一个血量和攻击力都小于她当前属性的怪物。
  2. 对于第一个击败的怪物,需要满足其血量小于 HH 且攻击力小于 AA。
  3. 击败怪物后,小E会获得该怪物的属性值。
  4. 为了保持战斗节奏,要求击败的怪物序列中,后一个怪物的血量和攻击力都必须严格大于前一个怪物。

小E想知道,她最多能击败多少怪物。


测试样例

样例1:

输入:n = 3, H = 4, A = 5, h = [1, 2, 3], a = [3, 2, 1]
输出:1

样例2:

输入:n = 5, H = 10, A = 10, h = [6, 9, 12, 4, 7], a = [8, 9, 10, 2, 5]
输出:2

样例3:

输入:n = 4, H = 20, A = 25, h = [10, 15, 18, 22], a = [12, 18, 20, 26]
输出:3


思路

目标:判断最多能够击杀多少只怪物。

  1. 初始条件检查
    • 首先,检查是否有怪物满足小E的初始条件(血量小于 H 且攻击力小于 A)。
  2. 动态规划
    • 使用动态规划来记录小E在击败每个怪物后的状态,并尝试找到一个最长的严格递增的怪物序列。
  3. 状态转移
    • 对于每个怪物,检查是否可以击败它,并且击败它后是否能形成一个更长的严格递增序列。

具体做法

  1. 初始条件检查

    • 遍历所有怪物,检查每个怪物的血量和攻击力是否都小于小E的初始血量 H 和攻击力 A
    • 如果满足条件,标记该怪物可以被击败。
        # 初始化一个数组来记录每个怪物是否可以被击败
        can_defeat = [False] * n
    
        # 检查每个怪物是否可以被初始状态的小E击败
        for i in range(n):
            if h[i] < H and a[i] < A:
                can_defeat[i] = True
    
  2. 动态规划数组

    • 使用动态规划来记录小E在击败每个怪物后的状态,并尝试找到一个最长的严格递增的怪物序列。
    • 动态规划数组 dp[i] 表示以第 i 个怪物结尾的最长严格递增序列的长度。。
    • 初始化 dp 数组为 1,因为每个怪物本身至少可以形成长度为 1 的序列。
        dp = [1] * n
    
  3. 状态转移

    • 对于每个怪物 i,检查所有之前的怪物 j
    • 如果 j 可以被击败且 i 的属性严格大于 j,则更新 dp[i] 为 dp[j] + 1
    • 通过这种方式,逐步构建最长的严格递增序列。
    for i in range(n):        # 遍历每个怪物
        if can_defeat[i]:            # 如果当前怪物可以被击败
            for j in range(i):       # 检查所有之前的怪物
                # 如果之前的怪物可以被击败,并且当前怪物的属性(攻击力和血量)严格大于之前的怪物
                if can_defeat[j] and h[i] > h[j] and a[i] > a[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
    # 返回 dp 数组中的最大值
    return max(dp)
    
  4. 返回结果

    • 返回 dp 数组中的最大值,即小E最多能击败的怪物数量。

完整代码

def solution(n: int, H: int, A: int, h: list, a: list) -> int:
    # 初始化一个数组来记录每个怪物是否可以被击败
    can_defeat = [False] * n
    
    # 检查每个怪物是否可以被初始状态的小E击败
    for i in range(n):
        if h[i] < H and a[i] < A:
            can_defeat[i] = True
    
    # 动态规划数组,dp[i] 表示以第 i 个怪物结尾的最长严格递增序列的长度
    dp = [1] * n
    
    # 遍历每个怪物
    for i in range(n):
        # 如果当前怪物可以被击败
        if can_defeat[i]:
            # 检查所有之前的怪物
            for j in range(i):
                # 如果之前的怪物可以被击败,并且当前怪物的属性(攻击力和血量)严格大于之前的怪物
                if can_defeat[j] and h[i] > h[j] and a[i] > a[j]:
                    # 更新 dp[i]
                    dp[i] = max(dp[i], dp[j] + 1)
    
    # 返回 dp 数组中的最大值
    return max(dp)

if __name__ == '__main__':
    print(solution(3, 4, 5, [1, 2, 3], [3, 2, 1]) == 1)
    print(solution(5, 10, 10, [6, 9, 12, 4, 7], [8, 9, 10, 2, 5]) == 2)
    print(solution(4, 20, 25, [10, 15, 18, 22], [12, 18, 20, 26]) == 3)

知识点整理

  1. 动态规划(Dynamic Programming)

    • 动态规划是一种通过将问题分解为子问题存储子问题的解来解决复杂问题的方法
    • 在本题中,我们使用动态规划来记录每个怪物被击败后的最长严格递增序列的长度。
  2. 状态转移方程

    • 状态转移方程是动态规划中的关键部分,它描述了如何从子问题的解推导出原问题的解。
    • 在本题中,状态转移方程为 dp[i] = max(dp[i], dp[j] + 1),表示如果怪物 i 可以被击败且属性严格大于怪物 j,则更新 dp[i]
  3. 数组操作

    • 使用数组来存储和操作数据是编程中的基本技能。
    • 在本题中,我们使用数组 can_defeat 来记录每个怪物是否可以被击败,使用数组 dp 来记录最长严格递增序列的长度。
  4. 循环和条件判断

    • 使用循环和条件判断来遍历和处理数据是编程中的基本操作。
    • 在本题中,我们使用嵌套循环来遍历怪物列表,并使用条件判断来检查怪物的属性是否满足要求。

感受

更熟悉动态规划了。