[M贪心] lcLCP30. 魔塔游戏(STL优先队列+堆+贪心)

本文介绍了一道关于贪心算法的题目——魔塔游戏。通过顺序累加怪物血量判断是否能通关,如果不能则返回-1。否则,使用贪心策略调整负血量怪物顺序,确保调整次数最少。具体策略是当血量不足时,将造成最大伤害的负血量怪物放到最后,并记录调整次数。最后返回调整次数作为答案。时间复杂度为O(nlogn),空间复杂度为O(n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 题目来源

链接:LCP 30. 魔塔游戏

2. 题目解析

挺不错的一道贪心问题。

思路:

  • 首先顺序累加所有怪物血量,在血量初始值为 1 时,若累加完毕, s u m ≤ 0 sum \le0 sum0 则说明怎么调整也是无法保证走完所有房间且血量符合要求的。返回 -1 即可。反之,一定可以通过调整走完所有房间,大不了就将所有的负数全部放到最后,就可以了。
  • 但是如何保证调整次数最少呢?一定不能见到一个负数就操作一次吧。
  • 贪心调整思路如下:
    • 每次在我们血量 b l o o d ≤ 0 blood \le 0 blood0 的时我们进行调整。调整时,将之前我们遇到的对我们伤害最大的怪物放到最后。即负数最小的,也就是负数中绝对值最大的怪物放到最后。因为之前遇到过,血量已经被他扣了,在调整时,要将之前被这个怪物扣的血量补充回来。直到依次将扣血最大的怪物向后调整,补充血量到 > 0 \gt0 >0 后,再依次向后走。
    • 血量初始为 1 ,为题目要求。顺序遍历怪物,记录遇到的负血量的怪物,由于需要动态的记录这些负血量怪物的最大值,所以需要使用优先队列。
    • 每遇到一个怪物,不论血量正负,都和当前的血量 b l o o d blood blood 进行累加。且记录负血量怪物进小顶堆,在此使用技巧,负值可以添加负号变为正值,然后放入大顶堆,达到和小顶堆同样的效果。注意出堆时,需要再补充回一个负号。
    • b l o o d ≤ 0 blood\le0 blood0 时,一定是之前遇到了负血量怪物,那么就要将堆中这些负血量怪物,按照扣出血量大小依次出堆,补充给 b l o o d blood blood,直到将 b l o o d blood blood 补充回 > 0 \gt0 >0 的状态。然后就不必再调整了。
    • 在此,直接取堆顶元素补充给血量即可,然后直接将堆顶元素出堆,不需要再保留该怪物的血量作记录。因为一定可以经过调整遍历完所有怪物房间,且只会有负血量怪物调整到末尾。所以,我们在血量见底时,假装的没遍历,调整后血量补充,等价于跳过这个扣血最大的怪物,将其放到最后。那么,遍历完之后我们剩余的血量 b l o o d blood blood,也是一定可以将最后这些调整的连续负血量怪物遍历完的。最后只需要返回最小的调整次数即可,剩余血量,就是一开始累加的 s u m sum sum

总结如下:

  • 一开始累加所有怪物血量,若 ≤ 0 \le0 0,则返回 -1,无法调整。
  • 反之,一定可以调整。顺序遍历所有怪物,累加血量 b l o o d blood blood,并将负血量怪物记录进优先队列,用以动态计算扣血最大的负血量怪物。当血量 b l o o d ≤ 0 blood\le0 blood0,则从堆中依次弹出之前遇到的扣血最大的怪物用以回血。这样一来,之前扣的血,现在再加上,等价于没遍历这个怪物,等价于将其调整到最后,则调整次数 +1。
  • 当遍历到数组末尾时,该末尾后面全是调整过来的负血量怪物,已经不需在此调整。一定保证当前剩余血量可以遍历完这些连续的负血量怪物,故直接返回调整次数即可。

注意开 LL


  • 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
  • 空间复杂度 O ( n ) O(n) O(n)

代码:

class Solution {
public:
    int magicTower(vector<int>& nums) {
        typedef long long LL;
        
        LL sum = 1;
        for (int x : nums) sum += x;
        if (sum <= 0) return -1;
        
        LL blood = 1, res = 0;
        priority_queue<int> q;
        for (int x : nums) {
            if (x < 0) q.push(-x);
            blood += x;
            while (blood <= 0) {
                blood += q.top(); q.pop();
                res ++ ;
            }
        }

        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值