6.小E的怪物挑战<字节青训营-中等题>

140 阅读4分钟

1.题目

问题描述

小E在一个游戏中遇到了nn个怪物。每个怪物都有其特定的血量hihi和攻击力aiai。小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

2.思路

这道题目综合运用了动态规划和贪心算法知识,是一道典型的最长递增子序列(LIS)问题,适合练习动态规划和贪心算法的同学。

解决思路:

1. 筛选可击败的怪物

  • 首先,我们遍历所有怪物,筛选出那些可以击败的怪物。一个怪物必须满足其血量小于小E当前的血量 H,并且其攻击力小于小E的攻击力 A
  • 通过 zip(h, a) 将怪物的血量和攻击力配对,筛选出符合条件的怪物。

2. 动态规划思路

  • 假设我们已经筛选出了一个新的怪物列表 t,这个列表包含了所有小E能击败的怪物。
  • 对于每一个怪物,我们记录一个状态 f[i],表示从怪物 0i 小E能够击败的最大怪物数。 0i 最少击败1个,即第一个。
  • 状态转移:对于每个怪物 i,我们尝试与之前的所有怪物 j 进行比较,如果怪物 j 的血量和攻击力都小于怪物 i,并且小E可以击败怪物 j,那么我们可以更新 f[i],即 f[i] = max(f[i], f[j] + 1),表示小E击败怪物 j 后再击败怪物 i

3. 求解最大值

  • 动态规划数组 f 中的每个值表示了小E击败某个怪物后能够获得的最大击败怪物数。最后,我们返回 f 中的最大值,即小E能够击败的最大怪物数。

3.代码

我的代码

def solution(n: int, H: int, A: int, h: list, a: list) -> int:
    # write code here
    # 删选出血量和攻击力都小于小E的怪物,存放在列表t中
    t = [(x, y) for x, y in zip(h, a) if x < H and y < A]
    m = len(t)
    # f[i],表示从怪物 0 到 i 小E能够击败的最大怪物数。
    f = [1 for _ in range(m)]
    for i in range(m):
        # j表示之前的所有怪物
        for j in range(i):
            # 如果第i个的攻击力和血量都比第j个高,则可以击败
            if t[i][0] > t[j][0] and t[i][1] > t[j][1]:
                # f[j] + 1表示先击败j,再击败i
                f[i] = max(f[i], f[j] + 1)
    return max(f)

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)

参考代码

def solution(n: int, H: int, A: int, h: list, a: list) -> int:
    assert n == len(a) == len(h)
    t = [(x, y) for x, y in zip(h, a) if x < H and y < A]
    m = len(t)
    f = [1 for _ in range(m)]
    for i in range(m):
        for j in range(i):
            if t[j][0] < t[i][0] and t[j][1] < t[i][1]:
                f[i] = max(f[i], f[j] + 1)
    assert m > 0
    return max(f)

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

t = [(x, y) for x, y in zip(h, a) if x < H and y < A

zip(h, a) 会将列表 h(怪物的血量)和列表 a(怪物的攻击力)中的对应元素一一配对,生成一个元组的迭代器。例如,如果 h = [1, 2, 3]a = [3, 2, 1],则 zip(h, a) 会生成以下的元组对:

[(1, 3), (2, 2), (3, 1)]