题目描述

✅ 1204. 最后一个能进入巴士的人

image-20260929075312476

image-20260929075312613

题意分析

Queue 表中每行是一名候车乘客,turn 决定上车先后,weight 是体重。巴士总载重不得超过一千千克,需要返回最后一位能够按队列顺序上车的乘客姓名。

不能跳过队伍中间的人去选择更轻的乘客,也不是寻找单人体重最大的人。判断某人能否上车,必须计算从队首到他这一整段的累计体重;刚好达到一千仍然允许。题目保证第一位乘客能够上车。

解法:SQL 查询建模

核心思路

[!blue]

按 turn 排序后,每个人对应一个“包含当前乘客的队伍前缀”。把这个前缀的体重求和,就得到了他上车之后的总重。随着后续乘客加入,总重不会下降,所以可以上车的人恰好组成队伍的一段前缀。

使用 SUM(weight) OVER (...) 计算累计体重。窗口函数为每位乘客保留原来的一行,并附加这个前缀总和;它不会像直接聚合那样把整队乘客压成一个总数。窗口内按 turn 排序,范围明确写成从第一行到当前行,确保当前乘客的体重也包含在判断中。

把窗口结果作为派生表 q,外层再用 total_weight <= 1000 筛选。这样筛选发生在累计值生成之后;不能在同一层的 WHERE 中直接使用尚未计算的窗口结果,也不能先过滤单人体重再对剩下的人累加,那会改变实际排队前缀。

筛选后留下的都是能上车的乘客,再按 turn 降序取第一行,就是合法前缀中顺位最后的一位。内层升序负责累计,外层降序负责选出末尾,两种排序的目的不同。最终只输出要求的 person_name。

解题步骤

  1. 内层保留姓名和顺位,并计算 SUM(weight) OVER (ORDER BY turn ...)。
  2. 将窗口范围设为 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,累加队首到当前人的体重。
  3. 外层筛选累计值不超过一千的行。
  4. 按 turn DESC 排序并使用 LIMIT 1,返回最后一位合法乘客的姓名。

代码实现

SELECT person_name
FROM (
    SELECT
        person_name,
        turn,
        -- 保留每位乘客,同时计算从队首到当前人的累计体重。
        SUM(weight) OVER (
            ORDER BY turn
            -- 窗口包含当前乘客,累计值是他上车之后的总重。
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS total_weight
    FROM Queue
) AS q
-- 在窗口值生成后筛选,恰好达到上限仍可上车。
WHERE total_weight <= 1000
-- 从合法前缀中取最后一位乘客。
ORDER BY turn DESC
LIMIT 1;

复杂度分析

  • 时间复杂度:常见需要排序的执行计划为 $O(n\log(n+1))$,窗口累计与筛选是线性处理;实际是否利用顺位索引、如何执行外层取首行,由数据库执行计划决定。
  • 空间复杂度:排序、派生表和窗口缓冲在常见实现中最坏为 $O(n)$,具体取决于执行计划及是否物化。

关键点总结

[!green]

  • 窗口函数保留每个乘客,同时计算包含他本人的累计体重。
  • 先生成累计值,再筛选合法前缀,最后按顺位选择末尾。
  • 顺位列而非人员编号或姓名决定上车顺序。

易错点总结

[!yellow]

  • 窗口不包含当前行,会用上车之前的总重判断当前人,错误接纳超重者。
  • 条件写成严格小于一千,会排除刚好满载的合法乘客。
  • 按单人体重筛选或先跳过某些乘客,会改变题目要求的连续上车顺序。
  • 外层升序后取第一行,得到的是最先上车的人,而不是最后一位。
  • 只对整表求一个体重总和,无法保留每名乘客对应的累计位置。

相似题目

题目 难度 关联与区别
1308. 不同性别每日分数总计 中等 同样按给定顺序计算累计总量,本题再筛出累计重量不超过容量的最后一条排队记录。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2022/22314173
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!