C++ 模拟题 力扣495. 提莫攻击 题解 每日一题
文章目录
- 题目描述
- 为什么这道题值得练?
- 算法原理
- 代码实现时的细节
- 代码实现
- 总结
- 下题预告
题目描述
题目链接:力扣495. 提莫攻击
题目描述:
示例 1:
输入:timeSeries = [1,4], duration = 2
输出:4
解释:
第 1 秒攻击后,中毒状态持续到第 1+2-1 = 2 秒(共 2 秒);
第 4 秒攻击后,中毒状态持续到第 4+2-1 = 5 秒(共 2 秒);
总中毒时长为 2+2 = 4 秒。
示例 2:
输入:timeSeries = [1,2], duration = 2
输出:3
解释:
第 1 秒攻击后,中毒状态本应持续到第 2 秒;
但第 2 秒提莫再次攻击,中毒状态刷新为持续到第 3 秒;
总中毒时长为 3 秒(从第 1 秒到第 3 秒)。
提示:
1 <= timeSeries.length <= 10^4
0 <= timeSeries[i], duration <= 10^7
timeSeries 按 非递减 顺序排列(题目保证输入有序,无需额外排序)
为什么这道题值得练?
这道题是 “区间重叠问题”的简化版 ,也是笔试中高频出现的“场景理解类模拟题”。它的核心难点不在于算法复杂度,而在于“把游戏场景转化为数学逻辑”——比如理解“后一次攻击会刷新中毒时间,导致前后两次中毒区间可能重叠”。
练这道题能帮你:
- 培养“场景转逻辑”的能力:很多算法题都有生活化/游戏化背景,学会剥离场景、提炼核心问题(本题即“计算多个可能重叠的区间总长度”)是关键,通过简单题来锻炼我们准确的捕捉题目中的细节,快速精准的模拟。
- 强化“边界处理”意识:比如“最后一次攻击的中毒时长无需判断重叠,直接加duration”,这类细节容易漏。
- 掌握“线性遍历”的高效写法:时间复杂度O(n)、空间复杂度O(1)的解法,减少代码体量。
算法原理
要解决这道题,核心是搞懂“两次攻击的中毒区间是否重叠”——这是决定总时长计算方式的关键。
那么我们可以通过一个数学的规律性的方法来进行下简化,就能很轻松的判断是否重叠的问题。
1.判断中毒区间是否重叠
假设我们第一次中毒时间是 a
,第二次中毒的时间是 b
,中毒持续时间是 d
那么如下图👇:
即相邻两次的中毒间隔(b - a
)与 中毒持续时间(duration
)进行判断就可以快速的将情况分类。
2. 分情况计算中毒时间
通过第一步的判断,我们就可以轻易来进行分类统计
情况1:不重叠
例:timeSeries = [1,4]
、duration=2
第1次结束时间=1+2-1=2,第2次开始时间=4 → 2 < 4,无重叠。
此时第 i
次攻击的贡献是完整的 duration
秒。
情况2:重叠
例:timeSeries = [1,3]
、duration=4
第1次结束时间=4,第2次开始时间=3 → 4 >= 3,有重叠。
因为再次攻击会重置中毒计数时间,所以此时第 i
次攻击的贡献是“从第i次开始到第i+1次开始前”的时间,即 timeSeries[i+1] - timeSeries[i]
秒。
3. 处理最后一次攻击
无论前面的攻击是否重叠,最后一次攻击的中毒时长一定是完整的 duration
秒——因为没有后续攻击来“打断”它的中毒时间。这是容易被忽略的关键边界,也是代码中需要单独处理的部分。
代码实现时的细节
虽说这奥体的思路很简单,但是有几个在实现代码的时候要注意的细节:
细节1:遍历范围
因为我们要比较“当前攻击”和“下一次攻击”,所以遍历只需要到 timeSeries.size()-2
(即倒数第二次攻击)。比如数组长度为 n
,索引从 0
到 n-1
,我们只遍历 0
到 n-2
,这样 i+1
不会越界。
细节2:处理特殊情况
当 duration = 0
时:无论攻击多少次,中毒时长都是 0(代码中 ret + 0
自然成立,无需额外判断)
当 timeSeries
只有一个元素时:遍历不会进入循环,直接返回 0 + duration
(正好是正确结果)。
代码实现
class Solution {
public:int findPoisonedDuration(vector<int>& timeSeries, int duration) {int ret = 0;// 遍历到倒数第二次攻击(i+1不越界)for (int i = 0; i < timeSeries.size() - 1; i++) {// 比较两次攻击的间隔与duration:取较小值作为当前攻击的贡献if (timeSeries[i + 1] - timeSeries[i] >= duration) {ret += duration;} else {ret += timeSeries[i + 1] - timeSeries[i];}}// 最后一次攻击的贡献是完整的durationreturn ret + duration;}
};
代码性能分析:
- 时间复杂度O(n):仅遍历一次数组(n为攻击次数),效率极高;
- 空间复杂度O(1):只使用了一个变量
ret
存储结果,无额外空间消耗; - 逻辑清晰:区分“中间攻击”和“最后一次攻击”,避免了边界错误;
- 鲁棒性强:自然处理了
duration=0
、timeSeries长度为1
等特殊情况。
总结
解决“场景类模拟题”,关键在于“先拆解逻辑,再写代码”。以这道题为例,我们可以提炼出三个通用步骤:
- 场景转模型 🧩:把“中毒时间”转化为“区间”,把“攻击刷新中毒”转化为“区间重叠判断”;
- 分情况讨论 📊:明确“重叠”和“不重叠”两种情况的计算规则,避免逻辑混乱;
- 抓边界特例 🚧:单独处理“最后一次攻击”“特殊输入(如duration=0)”,防止代码漏洞。
掌握这种“从场景到逻辑、从通用到特例”的思考方式,再遇到类似的模拟题(比如“合并区间”“任务调度”),就能快速找到解题思路。
下题预告
下一篇我们来练一道“场景化字符串模拟”的经典题——力扣6. Z字形变换。
这道题不像“提莫攻击”那样直接关联“区间”,而是需要我们先理解“Z字形排列”的空间场景,再把“排列规则”转化为字符串操作的逻辑——它的难点在于:
- 如何把“Z字形”这种可视化的排列方式,拆解成“行与列的移动规律”(比如“向下走到底后向上拐”“每一行字符的下标有什么规律”);
- 如何用简洁的代码模拟排列过程(避免用二维数组浪费空间,或因逻辑混乱导致下标越界);
- 如何通过“找规律”优化解法(从“模拟排列”升级到“直接计算每一行字符的位置”,进一步提升效率)。
这道题能帮我们进一步强化“场景转逻辑”的能力——尤其是面对“可视化场景”时,如何把抽象的图形规律,转化为可执行的代码逻辑。感兴趣的朋友可以先提前看看题目,试着在纸上画一画“Z字形排列”的过程,比如输入“PAYPALISHIRING”、行数3时的排列情况,提前感受下场景背后的规律~
如果这篇内容对你有帮助,别忘了 点赞👍 + 收藏⭐ + 关注👀 哦!有问题欢迎在评论区留言,我会认真思考并及时回复!