题解:学而思编程 排队
【题目来源】
学而思编程:排队
【题目描述】
某班有 \(n\) 个学生,编号 \(1\sim n\)。中午下课后,学生们陆续赶到食堂吃饭。食堂只有一个打饭窗口,所以学生们要排队打饭。
小猴是负责维护秩序的志愿者,他需要组织这些学生排成一条队伍买饭就餐。第 \(i\) 位学生的耐心指数为 \(a_i\),它的含义是:如果在 \(i\) 号之前,排队的人数超过了 \(a_i\),他就会放弃排队;否则他就会留下。
请问小猴应该如何安排这些学生的排队次序,才能使得留下吃饭的人最多?
【输入】
第一行一个整数 \(n\);
第二行 \(n\) 个整数 \(a_1,a_2,\dots,a_n\)。
【输出】
一行一个整数,表示留下吃饭的最大人数。
【输入样例】
5
4 1 0 2 1
【输出样例】
4
【核心思想】
-
问题分析:给定 \(n\) 个学生,每人有耐心指数 \(a_i\)(表示前面最多允许排 \(a_i\) 人,即该学生最多能排在第 \(a_i + 1\) 个位置)。需要安排排队顺序,使留下的人数最多。这是一个贪心排序问题,关键在于将耐心指数转化为位置约束后,按耐心指数升序排列并贪心选取。
-
算法选择:
- 升序排序:将耐心指数 \(a_i\) 从小到大排序
- 贪心选取:按排序后的顺序依次尝试安排,若当前已安排人数不超过 \(a_i\) 则留下
-
关键步骤:
- 初始化:读取 \(n\)(学生数量)和耐心指数数组 \(a[1..n]\)
- 升序排序:
sort(a + 1, a + n + 1),将耐心指数从小到大排列 - 贪心安排:
stay = 0:当前已留下的人数- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 若
stay \leq a_i:当前已留下的人数不超过该学生的耐心上限,该学生可以留下stay++:已留下人数加 \(1\)
- 否则:该学生前面人数已超过其耐心,无法留下,跳过
- 若
- 输出
stay
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),贪心遍历 \(O(n)\)
- 空间复杂度:\(O(n)\),存储耐心指数数组
-
贪心排序的核心思想:
- 约束转化:耐心指数 \(a_i\) 表示该学生最多能排在第 \(a_i + 1\) 个位置,即前面最多有 \(a_i\) 人。已安排
stay人时,该学生排在第stay + 1位,条件为stay \leq a_i - 交换论证:若存在最优解中某学生 \(x\) 的耐心指数大于某学生 \(y\) 但 \(x\) 排在 \(y\) 前面,交换两者顺序不会使 \(y\) 更不满足条件(因为 \(x\) 的耐心更大),可能使 \(x\) 更易满足,因此升序排列必为最优
- 贪心最优性:按耐心从小到大安排,确保每个被安排的学生都"刚好够用"其耐心,为后续耐心更大的学生留出空间
- 等价问题:该问题等价于"最多能选出多少个数,使得第 \(k\) 个被选中的数 \(\geq k-1\)",经典贪心策略
- 适用于"位置约束 + 顺序安排"的最优化问题,排序后贪心可保证全局最优
- 约束转化:耐心指数 \(a_i\) 表示该学生最多能排在第 \(a_i + 1\) 个位置,即前面最多有 \(a_i\) 人。已安排
【解题思路】

【算法标签】
贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, a[1000005], stay; // stay: 当前可保留的数的个数
int main()
{
cin >> n; // 输入n
for (int i = 1; i <= n; i++) // 输入数组
{
cin >> a[i];
}
sort(a + 1, a + n + 1); // 对数组排序
for (int i = 1; i <= n; i++) // 遍历排序后的数组
{
if (stay <= a[i]) // 如果当前可保留的数的个数≤a[i]
{
stay++; // 可以保留这个数
}
}
cout << stay << endl; // 输出最多可保留的数的个数
return 0;
}
【运行结果】
5
4 1 0 2 1
4
浙公网安备 33010602011771号