这里简单讲解一下小E的怪物挑战这道题,算是一道最长上升子序列的变种。
题目大意:
小E在一个游戏中遇到了 n 个按顺序出现的怪物,每个怪物都有其特定的血量 hi 和攻击力 ai。小E的初始血量为 H,攻击力为 A。 游戏规则如下:
- 小E可以击败一个血量和攻击力都小于她当前属性的怪物。
- 对于第一个击败的怪物,需要满足其血量小于 H 且攻击力小于 A。
- 击败怪物后,小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
样例解释:
击败顺序为14或24或25,注意第三个怪物无法击败
样例3:
输入:n=4, H=20, A=25, h=[10, 15, 18, 22], a=[12, 18, 20, 26]
输出:3
样例解释:
击败顺序为321,注意第四个怪物无法击败
求解思路
由题意可知,小E的攻击力和血量都在击败怪物的过程中递减,求最多击败的怪物数量即找到一个最长的递减子序列。
我们可以将这个序列反转就变成了寻找最长上升子序列。在这里需要两个序列同时上升才可以算作上升。
如果没有做过最长上升子序列问题的可以先去LeetCode看看300. 最长递增子序列 - 力扣(LeetCode)。
这里简单介绍一下最长上升子序列的思想。使用dp数组存储当前元素及其之前的长上升子序列的长度,使用两重循环,第一重循环从前往后枚举每个元素,第二重循环枚举该元素之前的每个元素,如果比该元素小就尝试更新dp数组。
时间复杂度O(n^2) 空间复杂度O(n)
下面是AC代码
def solution(n: int, H: int, A: int, h: list, a: list) -> int:
# write code here
monsters = [(0,0)]
monsters += [(h[i], a[i]) for i in range(n) if h[i] < H and a[i] < A]
n=len(monsters)
dp=[0 for i in range(n+2)]
for i in range(1,n):
dp[i]=1
for j in range(1,i):
if monsters[i][0]>monsters[j][0] and monsters[i][1]>monsters[j][1]:
dp[i]=max(dp[i],dp[j]+1)
return max(dp)