MarsCode AI 刷题思路解析:6. (中) 小E的怪物挑战 | 豆包MarsCode AI刷题

135 阅读9分钟

问题描述

小E在一个游戏中遇到了nn个怪物。每个怪物都有其特定的血量hih_i和攻击力aia_i。小E的初始血量为HH,攻击力为AA。她可以击败那些血量和攻击力都小于她自身的怪物。每击败一个怪物后,小E的血量和攻击力会变为该怪物的血量和攻击力。小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

问题分析

小E需要击败一系列怪物,每个怪物有特定的血量和攻击力。小E的初始血量和攻击力是已知的。每次击败一个怪物后,小E的血量和攻击力会变为该怪物的血量和攻击力。小E只能击败那些血量和攻击力都小于她自身的怪物。目标是计算小E最多能击败多少怪物。

1. 问题建模

  • 输入

    • 怪物数量 nn
    • 小E的初始血量 HH
    • 小E的初始攻击力 AA
    • 怪物的血量数组 hh
    • 怪物的攻击力数组 aa
  • 输出:小E能击败的最多怪物数量。

  • 约束条件

    • 小E只能击败那些血量和攻击力均小于自身的怪物。
    • 击败怪物后,小E的状态会更新为该怪物的血量和攻击力。

2. 解题思路

解决该问题可以分为以下几个步骤:

  1. 筛选怪物:选出所有可以被小E击败的怪物。
  2. 排序策略:对这些怪物排序,使得小E在击败一个怪物后,依然可以击败尽可能多的其他怪物。
  3. 状态更新:小E的状态更新为击败怪物后的血量和攻击力。
  4. 动态规划:记录击败某个怪物后的最优状态,避免重复计算。

3. 核心问题

  • 排序策略

    • 按血量排序:优先选择血量较小的怪物。
    • 按攻击力排序:优先选择攻击力较小的怪物。
    • 综合排序:优先选择血量和攻击力综合影响最小的怪物。
  • 动态规划状态转移

    • 如果小E在状态 (H,A)(H,A) 时击败怪物 (h[i],a[i])(h[i], a[i]),则状态转移为 (h[i],a[i])(h[i], a[i])

数据结构选择

可以使用一个列表来存储所有可被击败的怪物,每个怪物用一个元组表示,包含其血量和攻击力。

算法步骤

  1. 筛选可击败的怪物:首先,筛选出所有血量和攻击力都小于小E当前血量和攻击力的怪物。
  2. 排序策略选择:使用默认元组排序,即先按血量排序,再按攻击力排序。不过这种排序策略可能不是最优的,因为它没有考虑到击败怪物后小E的状态变化。
  3. 贪心选择:从小E当前状态开始,依次选择可以击败的怪物,并更新小E的状态,直到无法再击败更多怪物为止。

代码框架

根据上述思路,我们可以构建一个初步的代码框架:

def solution(n, H, A, h, a):
    # 收集所有可以击败的怪物
    monsters = [(h[i], a[i]) for i in range(n) if H > h[i] and A > a[i]]
    # 按照血量和攻击力排序
    monsters.sort()

    count = 0
    for mh, ma in monsters:
        # 只要小E可以击败该怪物,就更新状态
        if H > mh and A > ma:
            H, A = mh, ma  # 更新小E的状态
            count += 1
        else:
            break  # 无法击败更多怪物,结束
    return count

动态规划解法

由于没有考虑击败怪物后小E的状态变化,默认元组排序效果很差,想要得到最优解可采用动态规划思路。

1.状态定义

dp[i][j]dp[i][j] 表示小E当前血量为 ii、攻击力为 jj 时,最多能击败的怪物数量。

2.状态转移

对于每个怪物 (hk,ak)(h_k, a_k),如果小E的当前状态满足 H>hkH > h_kA>akA > a_k,则可以更新状态:

dp[hk][ak]=max(dp[hk][ak],dp[H][A]+1)dp[h_k][a_k] = \max(dp[h_k][a_k], dp[H][A] + 1)

3.初始状态

小E的初始状态为 (H,A)(H, A),即 dp[H][A]=0dp[H][A] = 0

4.终止条件

遍历所有状态后,最大值即为小E能够击败的最多怪物数量。

实现代码

以下是基于动态规划的 python 代码实现:

def solution(n, H, A, h, a):
    # 收集所有可以击败的怪物
    monsters = [(h[i], a[i]) for i in range(n) if H > h[i] and A > a[i]]
    
    # 初始化dp数组,假设最大血量和攻击力为1000
    max_hp = 1000
    max_atk = 1000
    dp = [[0] * (max_atk + 1) for _ in range(max_hp + 1)]
    
    # 初始状态
    dp[H][A] = 1
    
    # 动态规划
    for mh, ma in monsters:
        for i in range(max_hp, mh - 1, -1):
            for j in range(max_atk, ma - 1, -1):
                if dp[i][j] > 0:
                    dp[mh][ma] = max(dp[mh][ma], dp[i][j] + 1)
    
    # 找到最大值
    max_count = 0
    for i in range(max_hp + 1):
        for j in range(max_atk + 1):
            max_count = max(max_count, dp[i][j])
    
    return max_count - 1  # 减去初始状态的1


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)
    # 增加更多测试用例
    print(solution(4, 20, 25, [10, 15, 18, 22], [12, 18, 20, 26]) == 3)
    print(solution(4, 20, 25, [10, 15, 18, 22], [12, 18, 20, 26]) == 3)

样例输出

solution(3, 4, 5, [1, 2, 3], [3, 2, 1]) 输出为2,而不是预期的1。

Debug调试

逐步分析 solution(3, 4, 5, [1, 2, 3], [3, 2, 1]) 的执行过程:

  1. 初始状态dp[4][5] = 1

  2. 收集怪物monsters = [(1, 3), (2, 2)]

  3. 动态规划更新

    对于 (1, 3)dp[1][3] = max(dp[1][3], dp[4][5] + 1) = max(0, 1 + 1) = 2

    对于 (2, 2)dp[2][2] = max(dp[2][2], dp[4][5] + 1) = max(0, 1 + 1) = 2

  4. 最大值查找max_count = 2

  5. 返回结果max_count - 1 = 2 - 1 = 1

问题原因

从上述分析可以看出,dp[1][3] 和 dp[2][2] 都被更新为2,导致最终的 max_count 为2。这表明在动态规划过程中存在重复更新的问题。

改进后的代码

为了避免重复更新,使用一个集合来记录已经更新的状态,确保每个状态只被更新一次。

def solution(n, H, A, h, a):
    # 收集所有可以击败的怪物
    monsters = [(h[i], a[i]) for i in range(n) if H > h[i] and A > a[i]]
    
    # 初始化dp数组,假设最大血量和攻击力为1000
    max_hp = 1000
    max_atk = 1000
    dp = [[0] * (max_atk + 1) for _ in range(max_hp + 1)]
    
    # 初始状态
    dp[H][A] = 1
    
    # 动态规划
    for mh, ma in monsters:
        updated = set()  # 记录已经更新的状态
        for i in range(max_hp, mh - 1, -1):
            for j in range(max_atk, ma - 1, -1):
                if dp[i][j] > 0 and (i, j) not in updated:
                    dp[mh][ma] = max(dp[mh][ma], dp[i][j] + 1)
                    updated.add((mh, ma))
    
    # 找到最大值
    max_count = 0
    for i in range(max_hp + 1):
        for j in range(max_atk + 1):
            max_count = max(max_count, dp[i][j])
    
    return max_count - 1  # 减去初始状态的1


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)

样例输出

solution(4, 20, 25, [10, 15, 18, 22], [12, 18, 20, 26]) 输出为1,而不是预期的3。

Debug调试

逐步分析 solution(4, 20, 25, [10, 15, 18, 22], [12, 18, 20, 26]) 的执行过程:

  1. 初始状态dp[H][A] = 1 是正确的,表示初始状态下小E已经可以击败0个怪物。

  2. 收集怪物monsters = [(10, 12), (15, 18), (18, 20)]

  3. 动态规划更新

    对于 (10, 12)dp[10][12] = max(dp[10][12], dp[20][25] + 1) = max(0, 1 + 1) = 2

    对于 (15, 18)dp[15][18] = max(dp[15][18], dp[20][25] + 1) = max(0, 1 + 1) = 2

    对于 (18, 20)dp[18][20] = max(dp[18][20], dp[20][25] + 1) = max(0, 1 + 1) = 2

  4. 最大值查找max_count = 2

  5. 返回结果max_count - 1 = 2 - 1 = 1

问题定位

从上述分析可以看出,dp[10][12]dp[15][18] 和 dp[18][20] 都被更新为2,导致最终的 max_count 为2。这表明在动态规划过程中,可能存在重复更新的问题。

问题原因

动态规划的状态转移逻辑可能存在问题。我们需要确保在击败一个怪物后,小E的血量和攻击力会变为该怪物的血量和攻击力,并且能够继续击败其他怪物。

改进后的代码

为了避免重复更新,并且确保状态转移的正确性,我们可以使用一个队列来记录当前可以击败的怪物,并逐步更新状态。

from collections import deque

def solution(n, H, A, h, a):
    # 收集所有可以击败的怪物
    monsters = [(h[i], a[i]) for i in range(n) if H > h[i] and A > a[i]]
    
    # 初始化dp数组,假设最大血量和攻击力为1000
    max_hp = 100
    max_atk = 100
    dp = [[0] * (max_atk + 1) for _ in range(max_hp + 1)]
    
    # 初始状态
    dp[H][A] = 1
    
    # 使用队列来记录当前可以击败的怪物
    queue = deque([(H, A)])
    
    while queue:
        current_hp, current_atk = queue.popleft()
        for mh, ma in monsters:
            if current_hp > mh and current_atk > ma and dp[mh][ma] < dp[current_hp][current_atk] + 1:
                dp[mh][ma] = dp[current_hp][current_atk] + 1
                queue.append((mh, ma))
    
    # 找到最大值
    max_count = 0
    for i in range(max_hp + 1):
        for j in range(max_atk + 1):
            max_count = max(max_count, dp[i][j])
    
    return max_count - 1  # 减去初始状态的1

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)