Every day a Leetcode
题目来源:3273. 对 Bob 造成的最少伤害
解法1:贪心
首先,一直攻击同一个敌人,相比来回攻击多个敌人(雨露均沾)更好,因为这样我们被敌人攻击的次数更少。
从特殊到一般,想一想,如果只有两个敌人 A 和 B,我们应该先攻击谁?
消灭 A 需要攻击的次数为 kA = ceil(healthA / power),同理可得消灭 B 需要的攻击次数,记作 kB。
如果先消灭 A,再消灭 B,那么受到的伤害总和为 kA * damageA + (kA + kB) * damageB;如果先消灭 B,再消灭 A,那么受到的伤害总和为 kB * damageB + (kA + kB) * damageA。
如果先消灭 A,再消灭 B 更好,那么有 kA * damageA + (kA + kB) * damageB < kB * damageB + (kA + kB) * damageA,化简得 kA * damageB < kB * damageA。
推广到更多的敌人,可以按照上式对 damage 和 health 排序,理由如下。
先假定按照输入的顺序消灭敌人。如果发现相邻两个敌人不满足上面的不等式,就交换这两个敌人的位置,这可以让受到的总伤害变小。
不断交换敌人,直到所有相邻敌人都满足上面的不等式。
本质上来说,这个不断交换相邻敌人的过程,和冒泡排序是一样的。那么按照不等式对数组排序即可。
排序后,按照顺序消灭敌人。用一个变量 s 维护从一开始到击败当前敌人,所经过的秒数。把 s * damage[i] 加入答案。
代码:
/*
* @lc app=leetcode.cn id=3273 lang=cpp
*
* [3273] 对 Bob 造成的最少伤害
*/
// @lc code=start
class Solution
{
public:
long long minDamage(int power, vector<int> &damage, vector<int> &health)
{
int n = damage.size();
vector<pair<int, int>> comb(n);
for (int i = 0; i < n; i++)
{
int k = ceil(1.0 * health[i] / power);
comb[i] = make_pair(k, damage[i]);
}
sort(comb.begin(), comb.end(),
[&](const pair<int, int> &p1, const pair<int, int> &p2)
{ return p1.first * p2.second < p2.first * p1.second; });
long long ans = 0LL;
long long s = 0LL;
for (auto &[k, d] : comb)
{
s += k;
ans += s * d;
}
return ans;
}
};
// @lc code=end
结果:
复杂度分析:
时间复杂度:O(nlogn),其中 n 是数组 damage 的长度。
空间复杂度:O(n),其中 n 是数组 damage 的长度。