前言
记录一下做题思路,然后就是吐槽一下这道题,实在是太坑了。
笔者刷题刷得比较少,所以给出的思路可能不是最优思路,再加上最近事多,写得比较匆忙,难免有不少笔误,求大佬轻喷。
初步思路
问题
小E在一个游戏中遇到了n个怪物。每个怪物都有其特定的血量hi和攻击力ai。小E的初始血量为H,攻击力为A。她可以击败那些血量和攻击力都小于她自身的怪物。每击败一个怪物后,小E的血量和攻击力会变为该怪物的血量和攻击力。小E想知道,她最多能击败多少怪物。
分析样例
n = 5, H = 10, A = 10, h = [6, 9, 12, 4, 7], a = [8, 9, 10, 2, 5]
比如对上面的样例,我们先选择打第二个,此时h=9, a=9,接着打第一个和最后一个皆可,最后打第四个怪物,结果是三个。
分析
题目的标签是贪心,不过没想好怎么贪。
主要思路是(以下是我写出代码后,Marcode AI生成的分析,略做了一些调整)
- 排序:对怪物按血量进行了排序。
- 二分查找:使用二分查找找到了血量小于小E的初始血量最大的怪物,作为开始遍历的索引。
- 动态更新:每次击败一个怪物后,小E的血量和攻击力会变为该怪物的血量和攻击力。因此,我们需要动态更新小E的状态,并继续检查剩余的怪物。这里动态更新,我们使用动态规划进行计算。
dp
怪物的血量与怪物的攻击力并不相关。比如题目给出的第一个样例中,怪物血量等于3时,攻击力是1(最低的)。如果我们只选择血量最高的一路打下去,可能会因为攻击力过低提前寄,得不到最优解。比如h=[1,2,3], a=[1,2,1],如果我们选择第3个怪物打,攻击力变为1,就打不过剩下的怪物了。而如果我们选择先打第二个怪物,就可以接着打第一个,从而得到最大值为2。这就是我选择使用dp的原因(其实是我没想到怎么贪心)。
对于第i个怪物,我们需要分不同情况讨论。因为我们已经对怪物的血量进行了排序,且通过二分找到了血量小于小E的初始血量最大的怪物,第i个怪物的血量一定小于等于小E现有的血量。我们使用dp[i][a][h],表示剩下i个怪物,且小E血量、攻击力分别为h、a。
- 怪物血量等于小E现有血量,打不过,直接考虑剩下(i-1)个怪物,即
dp[i-1][a][h] - 怪物血量小于小E现有血量,且攻击力大于等于小E的攻击力,还是打不过,同上面的情况,即
dp[i-1][a][h] - 怪物血量小于小E现有血量,且攻击力小于小E的攻击力,这时候我们可以选择打与不打。不打的情况等同于上面两种情况的计算方式,即
dp[i-1][a][h]。如果打,小E血量攻击力变成怪物血量hi和攻击力ai,可击败怪物即为dp[i-1][ai][hi]+1,我们考虑两种情况的最大值。
代码框架
def solution(n: int, H: int, A: int, h: list, a: list) -> int:
# write code here
mon = []
for i in range(n):
mon.append([h[i], a[i]])
mon.sort(key=lambda x:x[0])
# 二分查找,查找首元素小于H的第一个元素
target = bin_search(mon, H)
mon = mon[:target + 1]
if not mon:
return 0
count = dp(mon, A, H, n)
return count
def bin_search(mon: list, H: int) -> int:
# 实现二分查找函数
# 返回第一个血量小于H的怪物的索引
left, right = 0, len(mon) - 1
while left <= right:
mid = (left + right) // 2
if mon[mid][0] < H:
left = mid + 1
else:
right = mid - 1
return right
def dp(arr, A, H, n):
if n == 0:
return 0
if arr[n - 1][1] >= A or arr[n - 1][0] >= H:
return dp(arr, A, H, n - 1)
return max(dp(arr, A, H, n - 1), dp(arr, arr[n - 1][1], arr[n - 1][0], n - 1) + 1)
出问题了?
思路很美好,但现实浇了一盆冷水。
没错,题目给出的第二个样例,结果是2,而根据我们的思路得出的结果是3。在我以为打表能通过时,又有第二个样例错了。就这样打表打了几个数据后,我陷入了沉思。后来求助了万能的群友,才知道,打怪要从最后一个怪物开始,按顺序打到第一个。
于是代码就变成了
def solution(n: int, H: int, A: int, h: list, a: list) -> int:
# write code here
count = dp([h, a], H, A, n)
print(count)
return count
def dp(arr, H, A, n):
if n == 0:
return 0
if arr[1][n - 1] >= A or arr[0][n - 1] >= H:
return dp(arr, H, A, n - 1)
return max(dp(arr, H, A, n - 1), dp(arr, arr[0][n - 1], arr[1][n - 1], n - 1) + 1)
吐槽
这个题库中的一些题目描述确实不太清楚(就比如这道题),不过毕竟这个功能是新开发的,还有很大的改进空间,希望可以做得越来越好。
不过用Marcode AI总结代码确实帮助我偷了懒,节省了一些我码字的时间(乐)