题解:学而思编程 食堂排队
【题目来源】
学而思编程:食堂排队
【题目描述】
某班有 \(n\) 个学生,编号 \(1\sim n\)。中午下课后,学生们陆续赶到食堂吃饭。食堂只有一个打饭窗口,所以学生们要排队打饭。
第 \(i\) 个学生在第 \(l_i\) 分钟开始排队(当然是排在队尾)。如果同一分钟有多个学生同时开始排队,则编号较小的学生排在编号较大的学生之前。当一名学生排在队伍的最前端时,即轮到该名学生打饭。每个学生打饭需要花费一分钟时间。打到饭的学生,立即离开队伍,没打到饭的学生,在队伍里继续等待。
每个学生的耐心都是有限的。对于第 \(i\) 个学生,如果在第 \(r_i\) 分钟时,他仍在排队,且未轮到他打饭(仍有人排在他之前),那么他就会直接离开,放弃打饭。
对于每个学生,请你确定他是在第几分钟开始打饭(即排到队首)的?
【输入】
第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。
每组数据第一行包含整数 \(n\)。
接下来 \(n\) 行,每行包含两个整数 \(l_i,r_i\),表示第 \(i\) 个学生到达队伍的时间以及忍耐的极限时间。
保证同一组数据的 \(l_i\) 是非递减的。
【输出】
每组数据输出一行结果,包含 \(n\) 个整数,其中第 \(i\) 个整数表示第 \(i\) 个学生开始打饭的时间,如果这个学生直接离开,放弃打饭,则输出 \(0\)。
【输入样例】
2
2
1 3
1 4
3
1 5
1 1
2 3
【输出样例】
1 2
1 0 2
【核心思想】
-
问题分析:给定 \(n\) 个学生,每个学生有到达时间 \(l_i\) 和忍耐极限 \(r_i\)。学生按到达时间排队(同分钟按编号),打饭需 \(1\) 分钟。若到 \(r_i\) 分钟仍未轮到自己则离开。求每个学生开始打饭的时间,或 \(0\)(离开)。这是一个贪心调度 + 区间覆盖问题,关键在于维护当前已分配的最大时间,为每个学生在其可用区间内分配最早的空闲时间。
-
算法选择:
- 贪心分配:维护当前已使用的最大时间
x,为每个学生分配满足条件的最小时间 - 区间判断:每个学生的可用区间为 \([l_i, r_i]\),若
x落在该区间内则分配x,否则调整
- 贪心分配:维护当前已使用的最大时间
-
关键步骤:
- 初始化:读取 \(T\)(测试组数)
- 处理每组数据:
- 读取 \(n\)
x = 0:当前已分配的最大时间(初始为 \(0\),表示尚未开始)- 遍历 \(n\) 个学生:
- 读取 \(l_i\) 和 \(r_i\)
- 若
x < l_i:- 输出
l_i:该学生在其到达时即可打饭 x = l_i + 1:更新已分配最大时间
- 输出
- 否则若
x > r_i:- 输出
0:该学生忍耐时间已过,无法打饭
- 输出
- 否则(
l_i <= x <= r_i):- 输出
x:该学生在当前最早可用时间打饭 x++:已分配最大时间推进 \(1\) 分钟
- 输出
- 输出换行
-
时间/空间复杂度:
- 时间复杂度:\(O(T \times n)\),每组数据线性处理
- 空间复杂度:\(O(1)\),仅需常数变量
-
贪心调度的核心思想:
- 最早可用时间原则:为每个学生分配不早于其到达时间、不晚于其忍耐时间的最早空闲时间,保证后续学生有最大可用空间
- 时间线维护:
x维护当前时间线的最右端,表示下一个可用时间点。若x < l_i说明有空档,从l_i开始;若x > r_i说明该学生已错过;否则x恰好可用 - 非递减到达的利用:题目保证 \(l_i\) 非递减,因此按顺序处理即可,无需排序或优先队列
- 打饭时间的连续性:每个打饭操作占用 \(1\) 分钟,
x的递增模拟了时间的线性推进 - 适用于"区间调度 + 贪心分配"类问题,维护当前时间线即可实现最优分配
【解题思路】

【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
int t, n, l, r, x; // t: 测试用例数, n: 区间个数, l,r: 区间左右端点, x: 当前已使用的最大值
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0); // 加速输入输出
cin >> t; // 输入测试用例数
while (t--) // 处理每个测试用例
{
cin >> n; // 输入区间个数
x = 0; // 初始化当前已使用的最大值
while (n--) // 处理每个区间
{
cin >> l >> r; // 输入区间[l, r]
if (x < l) // 如果当前最大值小于区间左端点
{
cout << l << " "; // 输出左端点
x = l + 1; // 更新当前最大值
}
else if (x > r) // 如果当前最大值大于区间右端点
{
cout << 0 << " "; // 输出0,表示无法选择
}
else // 当前最大值在区间内
{
cout << x << " "; // 输出当前最大值
x++; // 当前最大值加1
}
}
cout << endl; // 换行
}
return 0;
}
【运行结果】
2
2
1 3
1 4
1 2
3
1 5
1 1
2 3
1 0 2
浙公网安备 33010602011771号