题解:学而思编程 与怪物战斗

【题目来源】

学而思编程:与怪物战斗

【题目描述】

\(n\) 个怪物排队等待玩家来消灭它们。第 \(i\) 个怪物的体力为 \(h_i\)。玩家A的攻击力为 \(a\),玩家B的攻击力为 \(b\)

对于每只怪物,两位玩家按如下顺序交替行动,直到怪物血量小于等于 \(0\)

(1)玩家A攻击怪物,怪物血量减少 \(a\)。如果攻击后怪物血量小于等于 \(0\),玩家A得到 \(1\) 分。

(2)玩家B攻击怪物,怪物血量减少 \(b\)。如果攻击后怪物血量小于等于 \(0\),玩家A不得分。

每个怪都是由A先发起攻击。

玩家A另外有一种特殊能力,可以迫使B放弃一次攻击。他最多可以使用这个能力 \(k\) 次。

求出玩家A最多可以得到多少分。

【输入】

\(1\) 行,\(4\) 个整数 \(n,a,b,k\)

\(2\) 行,\(n\) 个正整数 \(h_1,h_2,⋯,h_n\)

【输出】

输出A能得到的最多分数

【输入样例】

6 2 3 3
7 10 20 12 1 8

【输出样例】

5

【核心思想】

  1. 问题分析:给定 \(n\) 个怪物,每个怪物体力为 \(h_i\)。玩家A攻击力为 \(a\),玩家B攻击力为 \(b\)。两位玩家交替攻击,A先手。A有 \(k\) 次特殊能力可以迫使B放弃攻击。求A最多能得到多少分(即击败多少怪物)。这是一个排序贪心问题,关键在于计算击败每个怪物需要的额外攻击次数,然后优先击败需要次数少的怪物。

  2. 算法选择

    • 攻击周期分析:每轮A和B共造成 \(a+b\) 点伤害,将怪物血量对 \((a+b)\) 取模得到当前周期的剩余血量
    • 额外攻击次数计算:根据剩余血量计算A需要发动多少次特殊能力才能确保击败该怪物
    • 排序贪心策略:按所需额外攻击次数升序排序,优先击败需要次数少的怪物,从而在有限技能点下最大化击败数量
  3. 关键步骤

    • 读取输入\(n, a, b, k\)(怪物数、A攻击力、B攻击力、技能点数)、\(h[1..n]\)(怪物体力)
    • 计算额外攻击次数(遍历每个怪物 \(i\)):
      • 计算剩余血量\(r = h_i \mod (a+b)\),若整除则 \(r = a+b\)
      • 计算需要次数
        • \(r \mod a = 0\),则 \(d[i] = r/a - 1\)
        • 否则 \(d[i] = \lfloor r/a \rfloor\)
    • 排序sort(d + 1, d + n + 1) 升序排序
    • 贪心选择(遍历 \(i\)\(1\)\(n\)):
      • 如果 \(k - d[i] \geq 0\),则 \(k -= d[i]\)cnt++
      • 否则跳出循环
    • 输出结果:最多可击败怪物数 \(cnt\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log n)\),主要是排序复杂度
    • 空间复杂度:\(O(n)\),存储怪物血量和额外攻击次数数组
  5. 排序贪心的核心思想

    • 问题转化:将复杂的战斗过程转化为"每个怪物需要多少技能点才能击败"的简单问题
    • 攻击周期分析:利用模运算分析攻击周期,简化问题
    • 贪心策略:优先选择消耗资源少的选项,在有限资源下最大化收益
    • 排序优化:通过排序将贪心选择转化为线性扫描,高效求解
    • 适用于资源分配、任务调度、有限预算下的最优化问题

【算法标签】

贪心

【代码详解】

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 5;
int n, a, b, k, c[N], d[N], cnt;  // c: 怪物血量, d: 需要的额外攻击次数, cnt: 可击败的怪物数

int main()
{
    cin >> n >> a >> b >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> c[i];
        int r;
        // 计算怪物在当前攻击循环中的剩余血量
        if (c[i] % (a + b) == 0)
        {
            r = a + b;  // 整除时相当于完整一轮
        }
        else
        {
            r = c[i] % (a + b);  // 余数
        }
        // 计算需要额外攻击的次数
        if (r % a == 0)
        {
            d[i] = r / a - 1;  // 恰好整除,需要r/a-1次额外攻击
        }
        else
        {
            d[i] = r / a;  // 向上取整减1
        }
    }
    // 对所需额外攻击次数排序
    sort(d + 1, d + n + 1);
    // 贪心选择:从需要额外攻击次数少的开始
    for (int i = 1; i <= n; i++)
    {
        if (k - d[i] >= 0)
        {
            k -= d[i];
            cnt++;
        }
        else
        {
            break;
        }
    }
    cout << cnt;
    return 0;
}

【运行结果】

6 2 3 3
7 10 20 12 1 8
5
posted @ 2026-06-14 10:45  团爸讲算法  阅读(8)  评论(0)    收藏  举报