题解:学而思编程 排队

【题目来源】

学而思编程:排队

【题目描述】

某班有 \(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

【核心思想】

  1. 问题分析:给定 \(n\) 个学生,每人有耐心指数 \(a_i\)(表示前面最多允许排 \(a_i\) 人,即该学生最多能排在第 \(a_i + 1\) 个位置)。需要安排排队顺序,使留下的人数最多。这是一个贪心排序问题,关键在于将耐心指数转化为位置约束后,按耐心指数升序排列并贪心选取。

  2. 算法选择

    • 升序排序:将耐心指数 \(a_i\) 从小到大排序
    • 贪心选取:按排序后的顺序依次尝试安排,若当前已安排人数不超过 \(a_i\) 则留下
  3. 关键步骤

    • 初始化:读取 \(n\)(学生数量)和耐心指数数组 \(a[1..n]\)
    • 升序排序sort(a + 1, a + n + 1),将耐心指数从小到大排列
    • 贪心安排
      • stay = 0:当前已留下的人数
      • 遍历 \(i\)\(1\)\(n\)
        • stay \leq a_i:当前已留下的人数不超过该学生的耐心上限,该学生可以留下
          • stay++:已留下人数加 \(1\)
        • 否则:该学生前面人数已超过其耐心,无法留下,跳过
    • 输出 stay
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),贪心遍历 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储耐心指数数组
  5. 贪心排序的核心思想

    • 约束转化:耐心指数 \(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\)",经典贪心策略
    • 适用于"位置约束 + 顺序安排"的最优化问题,排序后贪心可保证全局最优

【解题思路】

【算法标签】

贪心

【代码详解】

#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
posted @ 2026-07-04 14:20  团爸讲算法  阅读(5)  评论(0)    收藏  举报