有华为OD考试扣扣交流群可加:948025485
可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳od1336
了解算法冲刺训练
文章目录
- 题目链接
- 题目描述
- 解题思路
- 代码
- Python
- Java
- C++
- 时空复杂度
- 华为OD算法/大厂面试高频题算法练习冲刺训练
题目链接
LCP30、魔塔游戏
题目描述
小扣当前位于魔塔游戏第一层,共有 N
个房间,编号为 0 ~ N-1
。每个房间的补血道具/怪物对于血量影响记于数组 nums
,其中正数表示道具补血数值,即血量增加对应数值;负数表示怪物造成伤害值,即血量减少对应数值;0
表示房间对血量无影响。
小扣初始血量为 1,且无上限。假定小扣原计划按房间编号升序访问所有房间补血/打怪,为保证血量始终为正值,小扣需对房间访问顺序进行调整,每次仅能将一个怪物房间(负数的房间)调整至访问顺序末尾。请返回小扣最少需要调整几次,才能顺利访问所有房间。若调整顺序也无法访问完全部房间,请返回 -1。
示例 1:
输入:
nums = [100,100,100,-250,-60,-140,-50,-50,100,150]
输出:
1
解释:初始血量为 1。至少需要将 nums[3] 调整至访问顺序末尾以满足要求。
示例 2:
输入:
nums = [-200,-300,400,0]
输出:
-1
解释:调整访问顺序也无法完成全部房间的访问。
提示:
1 <= nums.length <= 10^5
-10^5 <= nums[i] <= 10^5
解题思路
非常有意思的题目。
考虑人脑在处理这个问题的时候是如何做的。
假设我们正常地从头到尾遍历整个数组nums
中的元素num
,维护变量cur
用于表示进入每一个房间之后的剩余血量。
当发现cur
的血量降到小于等于0
的时候,我们会想,如果在之前到达某个扣血扣得最多(绝对值最大的负数)的房间之前就事先把这个房间移动到末尾那就好了。
换句话说,当血量小于等于0
的时候,我们希望撤销之前扣得最多的那次扣血,使得我们现在的血量越高越高。
这就组成了一种带有反悔性质的贪心策略。
那么应该如何找到在此之前扣血最多的那个房间呢?很容易想到用优先队列来维护这个过程。答案就呼之欲出了。
代码
Python
from heapq import heappop, heappush# 贪心+优先队列:O(nlogn)
class Solution:def magicTower(self, nums: List[int]) -> int:ans = 0cur = 1# 优先队列储存已经访问过的负数visited = list()# 移动到末尾的负数的和remove_total = 0# 遍历所有数字for num in nums:# 将该数字加入cur中cur += num# 如果num是负数,则需要加入优先队列中if num < 0:heappush(visited, num)# 一旦发现cur降为0或0以下,则贪心地将前面访问过的最小的负数移动到数组末尾# 但并不需要显式地进行移动,只需要弹出visited堆顶元素remove_num即可# 同时cur回复之前扣除的血量,即减去remove_num# 由于remove_num被移动到末尾,将其加入remove_total中# 由于必须要做1次移动,因此更新ansif cur <= 0:remove_num = heappop(visited)cur -= remove_num remove_total += remove_numans += 1# 退出循环后,如果最后剩余血量cur加上被移动到末尾的血量remove_total仍然大于0# 则说明可以通过移动ans次房间,来顺利访问所有房间,# 否则返回-1,说明无论如何调整都无法访问所有房间,血量必然会扣为0return ans if cur + remove_total > 0 else -1
Java
class Solution {public int magicTower(int[] nums) {int ans = 0;long cur = 1;PriorityQueue<Integer> visited = new PriorityQueue<>(); // 小根堆long removeTotal = 0;for (int num : nums) {cur += num;if (num < 0) {visited.add(num);}if (cur <= 0) {int removeNum = visited.poll();cur -= removeNum;removeTotal += removeNum;ans++;}}return (cur + removeTotal > 0) ? ans : -1;}
}
C++
class Solution {
public:int magicTower(vector<int>& nums) {int ans = 0;long long cur = 1; // 使用long long 类型,防止溢出priority_queue<int, vector<int>, greater<int>> visited; // 小根堆long long remove_total = 0;for (int num : nums) {cur += num;if (num < 0) {visited.push(num);}if (cur <= 0) {int remove_num = visited.top();visited.pop();cur -= remove_num;remove_total += remove_num;ans++;}}return (cur + remove_total > 0) ? ans : -1;}
};
时空复杂度
时间复杂度:O(NlogN)
。入堆操作的时间复杂度为O(logN)
空间复杂度:O(N)
。堆所占空间。
华为OD算法/大厂面试高频题算法练习冲刺训练
-
华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
-
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
-
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
-
60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁
-
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
-
可查看链接 大厂真题汇总 & OD真题汇总(持续更新)
-
绿色聊天软件戳
od1336
了解更多