1 条题解
-
0
题目重述
有 只怪物,第 只怪物的战斗力为 。
玩家初始战斗力为 ,有 只拖鞋。两种操作:
- 击杀:若当前怪物战斗力 ,可以击杀它,击杀后 增加该怪物的当前战斗力。
- 扔拖鞋:向一只活着的怪物扔一只拖鞋,使其战斗力 。
目标:通过合理安排操作顺序,最大化最终战斗力 。
关键观察
- 击杀永远是有益的:只要能击杀,就应该击杀,因为击杀会提升 ,让你有能力击杀更强的怪物。
- 拖鞋的作用:给怪物扔拖鞋会增加它的战斗力,这看起来是坏事(更难击杀),但结合击杀来看:
- 如果一只怪物当前战斗力 ,我们本可以直接击杀,获得 的收益。
- 但如果先用拖鞋把它提升到 (需要 只拖鞋),再击杀,就能获得 的收益,比原来多了 。
- 所以拖鞋的本质是:用 只拖鞋换取未来击杀时多 点收益,但前提是你能在提升后击杀它。
- 拖鞋的时机:给怪物扔拖鞋的最佳时机,是在即将击杀它之前。因为如果太早扔,怪物变强可能会干扰击杀顺序,而且拖鞋数量有限。
- 击杀顺序:应该优先击杀战斗力小的怪物,因为它们容易满足条件,击杀后能提升 ,从而有能力处理更强的怪物。所以按怪物初始战斗力升序处理是合理的。
算法思路
将怪物按初始战斗力 从小到大排序。
按顺序处理每个怪物:
- 设当前战斗力为 。
- 如果当前怪物的初始战斗力 :
- 由于怪物已排序,后续怪物初始战斗力更大,不可能被击杀,直接结束。
- 如果当前怪物的初始战斗力 :
- 我们可以用拖鞋将它提升到 (如果拖鞋足够),这样击杀后 变为 。
- 提升需要的拖鞋数 。
- 实际使用的拖鞋数 ,因为拖鞋可能不够。
- 更新: 减去使用的拖鞋数, 增加 。
注意:即使拖鞋不够将怪物完全提升到 ,我们仍然可以击杀它(因为 ),只是收益少一些。
示例演示
以第六个测试用例为例:
n=5, c=18, k=30 a = [1, 2, 93, 84, 2]排序后:
步骤 当前 当前怪物 可用 提升到 需拖鞋 实际用拖鞋 新 新 初始 - - 怪物1 怪物2 怪物3 怪物4 - ,结束 - 最终 ,与输出一致。
正确性证明
贪心选择性质:
假设当前能击杀的最小怪物是 (),我们应该先用拖鞋提升它再击杀,而不是留给后面的怪物。
因为:- 提升它需要 只拖鞋,收益是让 翻倍(从 到 )。
- 如果留拖鞋给后面更大的怪物 ,提升它到 需要更多拖鞋(因为 ,所以 ),而击杀后的收益相同(都是 ),但更早获得 能让你更快击杀后面的怪物。
所以先处理小怪物是最优的。
最优子结构:
处理完前 个怪物后,剩余问题与原问题结构相同(剩余怪物按初始顺序,新的 和 ),因此贪心策略可以递归进行。
时间复杂度
- 排序:
- 遍历处理:
- 总复杂度:,,,完全可行。
代码实现
#include<bits/stdc++.h> using namespace std; int main() { int t; scanf("%d", &t); while (t--) { int n, k; long long C; scanf("%d %lld %d", &n, &C, &k); vector<int> a(n); for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } sort(a.begin(), a.end()); for (int i = 0; i < n; i++) { if (a[i] > C) break; // 杀不死了 int need = C - a[i]; // 提升到 C 需要的拖鞋数 int use = min(1ll * k, 1ll * need); k -= use; C += a[i] + use; } printf("%lld\n", C); } return 0; }
总结
这道题的核心是:
- 按怪物初始战斗力升序处理。
- 每次遇到可击杀的怪物,优先用拖鞋将其提升到当前战斗力再击杀,从而最大化收益。
- 拖鞋不够时,能提升多少就提升多少,但无论如何都能击杀(因为初始 )。
- 一旦遇到初始战斗力大于当前战斗力的怪物,后续都无法击杀,直接结束。
- 1
信息
- ID
- 6171
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者