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,则返回-1if 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)# 操作次数+1ans += 1return ans