题解:学而思编程 感应门
【题目来源】
学而思编程:感应门
【题目描述】
感应门会在有人经过的时候自动打开,冷却 \(d\) 秒后自动关闭。如果在门打开的时候有人通过,那么冷却时间会重置,重新冷却 \(d\) 秒后再关闭。
在一段时间内,有 \(n\) 个人陆续通过了感应门,他们通过感应门的时间点分别是 \(t_1,t_2,\cdots,t_n\)。请计算感应门一共开放了多少时间。
【输入】
第一行两个正整数 \(n, d\) ,\(n\) 表示通过感应门的人数,\(d\) 表示感应门的冷却时间;
第二行 \(n\) 个正整数 \(t_1,t_2,\cdots,t_n\),每个数字表达一个人通过感应门的时间点。
【输出】
一行一个整数,表示感应门总共开启了多少时间。
【输入样例】
7 3
1 2 7 10 15 17 22
【输出样例】
18
【核心思想】
-
问题分析:感应门在有人经过时打开,冷却 \(d\) 秒后关闭。若门打开期间有人通过,冷却时间重置。给定 \(n\) 个人通过的时间点,求门总开启时间。这是一个区间合并 + 贪心模拟问题,关键在于将连续的开启区间合并,避免重复计算。
-
算法选择:
- 维护当前区间:记录当前开门时间段
[open, close) - 事件驱动:按时间点顺序处理每个人,判断是延长当前区间还是开启新区间
- 维护当前区间:记录当前开门时间段
-
关键步骤:
- 初始化:
- 读取 \(n\)(人数)和 \(d\)(冷却时间)
- 读取第一个时间点 \(t\)
open = t:当前开门开始时间close = open + d:当前关门时间ans = 0:累计开启时间
- 处理后续事件(遍历 \(i\) 从 \(2\) 到 \(n\)):
- 读取下一个时间点 \(t\)
- 若
t < close(在关门前有新人通过):close = t + d:延长关门时间(冷却重置)
- 否则(关门后才有新人通过):
ans += close - open:累加前一段开启时间open = t:开启新时间段close = t + d
- 加上最后一段:
ans += close - open:处理最后一个区间的开启时间
- 输出
ans
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),线性遍历时间点
- 空间复杂度:\(O(1)\),仅需常数变量
-
区间合并的核心思想:
- 事件时间排序:时间点天然按顺序给出,无需额外排序
- 区间连续性判断:若新事件在
close之前,说明当前门仍开着,只需延长close;否则前一段结束,累加并开启新段 - 冷却重置机制:
close = t + d而非close + d,确保从最新事件时刻重新冷却 \(d\) 秒 - 最后区间处理:循环结束后最后一个区间尚未累加,需单独加上
- 适用于"时间区间合并 + 重置机制"类问题,贪心维护当前区间即可实现最优合并
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // 事件数量
int t; // 临时变量,用于输入时间
int x; // 每次开门后的持续时间
int ans; // 总开门时间
int main()
{
cin >> n >> x >> t; // 输入事件数量、持续时间、第一个事件时间
int open = t; // 当前开门开始时间
int close = open + x; // 当前开门结束时间
for (int i = 1; i <= n; i++)
{
cin >> t; // 输入下一个事件的时间
if (t < close) // 如果事件在关门之前发生
{
close = t + x; // 延长关门时间
}
else // 如果事件在关门之后发生
{
ans += close - open; // 累加前一段开门时间
open = t; // 新的开门开始时间
close = t + x; // 新的关门时间
}
}
// 加上最后一段开门时间
cout << ans + close - open << endl;
return 0; // 程序正常结束
}
【运行结果】
7 3
1 2 7 10 15 17 22
18
浙公网安备 33010602011771号