#LC2073. 买票需要的时间
买票需要的时间
你好,同学!欢迎来到 NOI 数据结构专项训练。
今天我们要攻克的是一个关于“队列模拟”与“贡献法思想”的入门好题:买票需要的时间。这道题虽然在 LeetCode 上被归类为简单题,但在 OI 竞赛中,它是理解“离散时间模拟”和“逻辑分类讨论”的绝佳素材。
一、 预备知识点
在动手之前,请确保你已经掌握以下内容:
- 队列(Queue)的逻辑:先进先出(FIFO)。虽然本题可以用数学方法直接解,但模拟法的核心是队列。
- 模拟(Simulation):按照题目描述的步骤,一步步在代码中复现过程。
- 取最小值函数
std::min:用于处理每个人实际贡献的买票次数。 - 循环与分支判断:处理排在目标人物之前和之后的逻辑差异。
二、 NOI 竞赛题目描述
题目名称:买票需要的时间 (Time Needed to Buy Tickets) 时间限制:1.0s 内存限制:256MB
【问题描述】 有 个人正在排队买票,其中第 个人排在队伍最前面,第 个人排在队伍最后面。
每个人买一张票需要消耗 秒。一旦一个人买到一张票,如果他还需要购买更多的票,他会立即走到队伍的最后面;如果他已经买完了所有需要的票,他就会离开队伍。
给定一个长度为 的整数数组 tickets,其中 tickets[i] 表示第 个人需要购买的票数。再给定一个整数 ,表示我们需要观察的目标人物的索引(从 开始)。
请计算并返回第 个人买完所有票所需要的总时间。
【输入格式】
输入包含两行。
第一行包含两个整数 和 ,分别表示人数和目标人物的索引。
第二行包含 个整数,表示 tickets 数组。
【输出格式】 输出一个整数,表示目标人物 买完票所需的总秒数。
【样例输入】
3 2
2 3 2
【样例输出】
6
【数据规模与约定】
三、 启发式思路引导:草稿纸上的推演
请拿出草稿纸,我们尝试两种思维方式:
1. 模拟思维(适合初学者)
- 动作:建立一个队列,不断弹出队首,让其票数减 1。
- 判断:如果票数还没减到 0,就重新压入队尾。
- 计数:每次弹出动作,时间 。
- 终止:当第 个人的票数变成 0 时,当前的 就是答案。
2. 贡献法思维(数学优化,竞赛常用)
我们要算第 个人买完票时总共过去了多少秒。这就等于:所有人在此期间一共买了几张票? 假设目标人物 需要买 张票:
- 对于排在 前面(或就是 自己)的人 :
在 买完 张票之前,这些人最多能买到几张?
答:如果他需要的票比 多,他会买 张;如果比 少,他会买完自己的票就走。
即贡献时间为:
min(tickets[i], V)。 - 对于排在 后面的人 :
在 买完第 张票的那一刻,排在后面的人还没来得及买第 轮票。
即贡献时间为:
min(tickets[i], V - 1)。
四、 复杂度分析与优化建议
- 时间复杂度:
- 模拟法:。本题数据范围 ,完全可以通过。
- 计算法:。只需遍历一遍数组,是这类题目的最优解。
- 空间复杂度:
- 只需存储数组,空间复杂度 。
优化建议: 在 NOI 考场上,若 达到 ,模拟法会超时(TLE),必须使用贡献法 解决。
五、 算法流程图(伪代码逻辑)
为了符合 C++14 竞赛风格且避免 Mermaid 渲染错误,我们使用文字描述节点逻辑:
graph TD
Start(开始处理任务) --> Input(读入 n, k 和 tickets 数组)
Input --> Init(初始化 totalTime 等于 0, 目标票数 V 等于 tickets_k)
Init --> LoopStart{遍历 i 从 0 到 n 减 1}
LoopStart -- 遍历结束 --> Output(输出 totalTime)
LoopStart -- 正在遍历 i --> Judge{判断 i 与 k 的位置关系}
Judge -- i 小于或等于 k --> CalcA(该人买票数等于 tickets_i 与 V 的较小值)
Judge -- i 大于 k --> CalcB(该人买票数等于 tickets_i 与 V 减 1 的较小值)
CalcA --> Accumulate(totalTime 累加该买票数)
CalcB --> Accumulate
Accumulate --> LoopStart
Output --> End(结束)
六、 读题关键词总结
- “走回队伍最后面”:这是循环队列或模拟队列的标志。
- “买完票就离开”:意味着该元素在模拟中将不再参与后续循环。
- “第 k 个人买完票”:这是终止条件,要注意第 轮买票时,排在他后面的人还没开始。
- “总时间”:在模拟中每步消耗 1 秒,在计算法中则是所有贡献的加和。
教练寄语: 这道题是训练“分类讨论”思维的良药。在 OI 竞赛中,很多看似需要复杂模拟的题目,只要你能站在“贡献”的角度去思考,往往能化繁为简。去写出你的 C++ 代码吧!