1.题目
这道题是2024-2-6的签到题,题目难度中等。
考核的知识点为:贪心算法+优先队列。
题目链接:魔塔游戏
小扣当前位于魔塔游戏第一层,共有 N
个房间,编号为 0 ~ N-1
。每个房间的补血道具/怪物对于血量影响记于数组 nums
,其中正数表示道具补血数值,即血量增加对应数值;负数表示怪物造成伤害值,即血量减少对应数值;0
表示房间对血量无影响。
小扣初始血量为 1,且无上限。假定小扣原计划按房间编号升序访问所有房间补血/打怪,为保证血量始终为正值,小扣需对房间访问顺序进行调整,每次仅能将一个怪物房间(负数的房间)调整至访问顺序末尾。请返回小扣最少需要调整几次,才能顺利访问所有房间。若调整顺序也无法访问完全部房间,请返回 -1。
2.思路
其实对于优先队列的问题,它其实已经包含了贪心算法的思想在里面,这道题也不例外。对于这道题,我们需要弄明白下面几个问题:
什么是优先队列?
优先队列相比于普通的队列,它会根据某种规则自动排序,比如根据队列元素的大小进行排序,小的在前面或者大的在前面。
通俗来讲,也有一个结构和它类似——小根堆。小根堆的特性是元素小的在上面,元素大的在下面(这里仅按照元素大小排序)。
如何应用到这题?
那我们如何使用优先队列在这题呢?根据题目要求,我们不到万不得已的时候不会移动怪物房间,因此我们只需要在当前的血量小于1的时候移动怪物房间,那移动哪一个怪物房间呢?这就提到我们的优先队列了,我们将扣血最多的怪物房间移动到队列尾部就行了,这样就即解决我们的血量问题,也能够使得利益最大化(贪心思想)。
解题思路
在我们对有限队列有了一定的认知后,我们在想怎么应用这题。我的思路是这样,这题的要求是尽可能最小移动次数来使得怪物房间移动到队尾。因此我们需要考虑两种情况:
- nums数组和小于0
- nums数组和大于0
对于第一种情况相信我们很容易理解,你都总和小于0了,说明无论怎么花里胡哨的移动,他最终都会ganmeover。
因此我们的算法只需要考虑第二种情况。首先,我们初始并定义一个小根堆,然后定义一个遍历blood作为血量,ans为操作次数。然后开始遍历数组,如果当前的num值小于0,说明这个房间是怪物房间,我们将它加入到小根堆(优先队列)里面。然后先加入到当前血量中,如果加入到当前血量后小于1,说明有个怪物房间需要移动到队尾了,因此这里需要移动扣血最多的怪物房间到队尾。
3.代码
import heapq
from typing import List
class S:
def f(self,nums:List[int])->int:
# 如果数组总和小于0,则返回-1
if sum(nums) < 0:
return -1
# 操作次数
ans = 0
# 小根堆
q = []
# 血量
blood = 1
# 初始化为小根堆
heapq.heapify(q)
# 遍历nums数组
for num in nums:
# 如果当前的值小于0,则加入小根堆
if num < 0:
heapq.heappush(q,num)
# 加入到血量
blood += num
# 如果血量小于1,说明需要移动房间
if blood < 1:
# 移动扣血最多的房间
blood -= heapq.heappop(q)
# 操作次数+1
ans += 1
return ans