题解:学而思编程 与怪物战斗
【题目来源】
学而思编程:与怪物战斗
【题目描述】
有 \(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
【核心思想】
-
问题分析:给定 \(n\) 个怪物,每个怪物体力为 \(h_i\)。玩家A攻击力为 \(a\),玩家B攻击力为 \(b\)。两位玩家交替攻击,A先手。A有 \(k\) 次特殊能力可以迫使B放弃攻击。求A最多能得到多少分(即击败多少怪物)。这是一个排序贪心问题,关键在于计算击败每个怪物需要的额外攻击次数,然后优先击败需要次数少的怪物。
-
算法选择:
- 攻击周期分析:每轮A和B共造成 \(a+b\) 点伤害,将怪物血量对 \((a+b)\) 取模得到当前周期的剩余血量
- 额外攻击次数计算:根据剩余血量计算A需要发动多少次特殊能力才能确保击败该怪物
- 排序贪心策略:按所需额外攻击次数升序排序,优先击败需要次数少的怪物,从而在有限技能点下最大化击败数量
-
关键步骤:
- 读取输入:\(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++ - 否则跳出循环
- 如果 \(k - d[i] \geq 0\),则 \(k -= d[i]\),
- 输出结果:最多可击败怪物数 \(cnt\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),主要是排序复杂度
- 空间复杂度:\(O(n)\),存储怪物血量和额外攻击次数数组
-
排序贪心的核心思想:
- 问题转化:将复杂的战斗过程转化为"每个怪物需要多少技能点才能击败"的简单问题
- 攻击周期分析:利用模运算分析攻击周期,简化问题
- 贪心策略:优先选择消耗资源少的选项,在有限资源下最大化收益
- 排序优化:通过排序将贪心选择转化为线性扫描,高效求解
- 适用于资源分配、任务调度、有限预算下的最优化问题
【算法标签】
贪心
【代码详解】
#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
浙公网安备 33010602011771号