题目链接
题目描述
在一个外卖站,有 个充电宝(编号 1 到
)和
位骑手(按输入顺序编号 1 到
)。骑手们会按各自的到达时间点来更换电池。
换电规则:
- 骑手按各自的到达时间先后顺序进行操作。
- 当一位骑手到达时,他会从当前所有可用的充电宝中,选择编号最小的一个来使用。
- 该充电宝被占用后,会进入充电状态,并在
当前时间 + 充电时间后再次变为可用。
你需要找出按输入顺序编号为 的那位骑手,最终使用了哪个编号的充电宝。
输入:
- 第一行:
。
- 接下来
行:按骑手编号 1 到
的顺序,每行给出该骑手的
到达时间和充电时间。
输出:
- 一个整数,代表编号为
的骑手所使用的充电宝编号。
解题思路
这是一个典型的事件驱动模拟问题。核心思想是不能按骑手的输入顺序处理,而必须严格按照时间顺序来模拟整个事件流程。我们需要高效地管理充电宝的两种状态:“可用”和“占用中”。
-
事件排序
- 首先,我们需要将所有骑手的换电请求视为一个个独立的事件。由于事件必须按时间顺序发生,所以第一步是将所有骑手的信息(包括他们的原始编号、到达时间、充电时间)读入后,按到达时间升序排序。
-
状态管理
- 我们需要两个数据结构来动态维护充电宝池的状态:
- 可用充电宝池: 存放当前所有可用的充电宝编号。根据规则,骑手总是选择编号最小的,因此一个最小堆(Min-Priority Queue) 是最理想的数据结构。
- 占用充电宝池: 存放正在充电的充电宝。我们需要知道它们何时会变回可用状态。因此,可以用另一个最小堆来存储
(可用时间点, 充电宝编号)的二元组,并按“可用时间点”排序。
- 我们需要两个数据结构来动态维护充电宝池的状态:
-
模拟流程
- 初始化:
- 将所有
个充电宝(编号 1 到
)放入“可用充电宝池”的最小堆中。
- “占用充电宝池”的最小堆初始为空。
- 将所有
- 处理事件: 遍历按时间排好序的骑手列表。对于每个骑手:
a. 推进时间: 获取当前骑手的到达时间
current_time。 b. 释放充电宝: 查看“占用充电宝池”堆顶的元素。如果堆顶充电宝的“可用时间点”小于或等于current_time,说明它在当前骑手到达时或之前已经充电完成。将其从占用堆中弹出,并将其编号放回可用堆。重复此步骤,直到占用堆为空或堆顶元素的可用时间点大于current_time。 c. 分配充电宝: 从“可用充电宝池”的堆顶取出一个元素,这就是当前骑手能拿到的编号最小的可用充电宝。 d. 记录结果: 如果当前骑手的原始编号是,那么上一步取出的充电宝编号就是我们要求的答案。 e. 更新状态: 计算新占用的充电宝将来的可用时间点
current_time + 充电时间,然后将(新可用时间点, 充电宝编号)存入“占用充电宝池”的最小堆中。
- 初始化:
-
输出:
- 遍历完所有骑手后,输出记录下的目标骑手
所使用的充电宝编号。
- 遍历完所有骑手后,输出记录下的目标骑手
代码
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
struct Rider {
int id;
int arrival_time;
int charge_time;
};
struct Occupied {
int free_time;
int bank_id;
bool operator>(const Occupied& other) const {
return free_time > other.free_time;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n, m, p;
cin >> n >> m >> p;
vector<Rider> riders(m);
for (int i = 0; i < m; ++i) {
riders[i].id = i + 1;
cin >> riders[i].arrival_time >> riders[i].charge_time;
}
sort(riders.begin(), riders.end(), [](const Rider& a, const Rider& b) {
return a.arrival_time < b.arrival_time;
});
priority_queue<int, vector<int>, greater<int>> available_banks;
for (int i = 1; i <= n; ++i) {
available_banks.push(i);
}
priority_queue<Occupied, vector<Occupied>, greater<Occupied>> occupied_banks;
int target_rider_bank_id = -1;
for (const auto& rider : riders) {
int current_time = rider.arrival_time;
while (!occupied_banks.empty() && occupied_banks.top().free_time <= current_time) {
available_banks.push(occupied_banks.top().bank_id);
occupied_banks.pop();
}
int chosen_bank = available_banks.top();
available_banks.pop();
if (rider.id == p) {
target_rider_bank_id = chosen_bank;
}
occupied_banks.push({current_time + rider.charge_time, chosen_bank});
}
cout << target_rider_bank_id << endl;
return 0;
}
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Collections;
import java.util.Comparator;
class Rider {
int id;
int arrivalTime;
int chargeTime;
Rider(int id, int arrivalTime, int chargeTime) {
this.id = id;
this.arrivalTime = arrivalTime;
this.chargeTime = chargeTime;
}
}
class Occupied {
int freeTime;
int bankId;
Occupied(int freeTime, int bankId) {
this.freeTime = freeTime;
this.bankId = bankId;
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int p = sc.nextInt();
List<Rider> riders = new ArrayList<>();
for (int i = 0; i < m; i++) {
riders.add(new Rider(i + 1, sc.nextInt(), sc.nextInt()));
}
riders.sort(Comparator.comparingInt(r -> r.arrivalTime));
PriorityQueue<Integer> availableBanks = new PriorityQueue<>();
for (int i = 1; i <= n; i++) {
availableBanks.add(i);
}
PriorityQueue<Occupied> occupiedBanks = new PriorityQueue<>(Comparator.comparingInt(o -> o.freeTime));
int targetRiderBankId = -1;
for (Rider rider : riders) {
int currentTime = rider.arrivalTime;
while (!occupiedBanks.isEmpty() && occupiedBanks.peek().freeTime <= currentTime) {
availableBanks.add(occupiedBanks.poll().bankId);
}
int chosenBank = availableBanks.poll();
if (rider.id == p) {
targetRiderBankId = chosenBank;
}
occupiedBanks.add(new Occupied(currentTime + rider.chargeTime, chosenBank));
}
System.out.println(targetRiderBankId);
}
}
import heapq
def main():
n, m, p = map(int, input().split())
riders = []
for i in range(m):
arrival, charge = map(int, input().split())
riders.append({'id': i + 1, 'arrival': arrival, 'charge': charge})
# 按到达时间对骑手排序
riders.sort(key=lambda r: r['arrival'])
# 可用充电宝,最小堆
available_banks = list(range(1, n + 1))
heapq.heapify(available_banks)
# 占用中充电宝 (free_time, bank_id),最小堆
occupied_banks = []
target_rider_bank_id = -1
for rider in riders:
current_time = rider['arrival']
# 释放到点的充电宝
while occupied_banks and occupied_banks[0][0] <= current_time:
_, bank_id = heapq.heappop(occupied_banks)
heapq.heappush(available_banks, bank_id)
# 分配编号最小的充电宝
chosen_bank = heapq.heappop(available_banks)
if rider['id'] == p:
target_rider_bank_id = chosen_bank
# 将新占用的充电宝放入占用堆
new_free_time = current_time + rider['charge']
heapq.heappush(occupied_banks, (new_free_time, chosen_bank))
print(target_rider_bank_id)
if __name__ == "__main__":
main()
算法及复杂度
- 算法:事件驱动模拟、优先队列(堆)
- 时间复杂度:
。
- 对
个骑手按到达时间排序需要
。
- 主循环遍历
个骑手。在循环内部,涉及对两个优先队列的操作。优先队列的大小最多为
,因此每次操作(入队/出队)的复杂度是
。总的模拟过程复杂度为
。
- 对
- 空间复杂度:
。
用于存储所有骑手的信息。
用于存储两个优先队列中的充电宝状态。

京公网安备 11010502036488号