深耕专业教研,探索前沿技术
信息技术来源:浙江强基联盟2026年3月高三联考

第 15 题

答案与解析

第(1)小题

参考答案
  1. 厨师1

  2. 18:30

厨师 0 烹制订单 1,到 18:25 已结束;厨师 1 此前未接单。18:30 时,两人都空闲且擅长川菜,而厨师 2 只能烹制粤菜。

时刻与事件 厨师 0 厨师 1
18:25,订单 1 完成后 空闲 空闲
18:30,先分配原有订单 3 接订单 3,至 19:00 空闲
18:30,再分配新增订单 7 忙碌 接订单 7

同一时刻,原有订单 3 先按最小编号原则交给厨师 0,新增订单 7 随后交给仍空闲的厨师 1,因此无需等待,实际开始时间为 18:30。

第(2)小题

参考答案

i > 0 and work[i][0] < work[i-1][0]

解题思路

新记录先追加到有序列表末尾,再通过与前一项比较、交换向左移动,这是插入排序中插入一个新元素的过程。排序关键字是结束时间,即每条记录中下标 0 的数据。

程序分析

def insert_work(end_time, chef):
    work.append([end_time, chef])
    i = len(work) - 1
    while work[i][0] > work[i-1][0]:
        work[i], work[i-1] = work[i-1], work[i]
        i -= 1

append() 把新记录放在末尾,i 指向它当前所在的位置。循环体每交换一次,新记录就向左移动一位,i 也减 1;因此 while 条件既要判断是否还有前一项,又要判断新记录是否需要继续向左移动。

填空分析

work[i][0] > work[i-1][0]

升序排列要求前一项的结束时间不大于后一项。只有当前记录的结束时间更小,即 work[i][0] < work[i-1][0] 时,才需要交换;原来的 > 会把较大的结束时间向前移动,方向相反。

同时必须限制 i > 0。i 为 0 时已经到达表首,work[i-1] 会访问末项,不能再把它当作前一项。把边界判断放在 and 左侧,可以在 i == 0 时短路,停止比较。正确条件为:

i > 0 and work[i][0] < work[i-1][0]

结束时间相等时不交换,保留这些记录原有的先后顺序。

第(3)小题

参考答案
  1. orders[tail[chef]][5] = oid

  2. chef = find_chef(tp)

  3. now += 1

程序分析

idle = [True] * n
work = []       # 工作队列
wait = []       # 等待队列
head = [-1] * n
tail = [-1] * n
i = 0
now = 0
while i < len(orders) or wait or work:
    ...
return head

主程序调用 arrange(orders, n, skills),开始调度。idle 中的 True 表示对应厨师空闲,work、wait 初始为空,head、tail 都以 -1 初始化。i 从第一个订单的下标 0 开始,now 从 0 分钟开始。只要还有未接收订单、等待订单或正在工作的厨师,外层循环就继续。

while i < len(orders) and con1(orders[i][1]) <= now:
    tp = orders[i][3]
    dur = orders[i][2]
    ②
    if chef != -1:
        assign(chef, i, now)
    else:
        wait.append([i, tp, dur])
    i += 1

内层循环按到达顺序处理时间不晚于 now 的订单,tp 取当前订单的菜品类型。空②产生后续分支使用的 chef:不是 -1 就调用 assign(chef, i, now),否则把订单下标、菜品类型和耗时放入 wait。处理后 i 加 1,转向下一条尚未接收的订单。这里 i、oid 保存的是 orders 的列表下标,与从 1 开始的订单编号不是同一个量。

遇到 assign(chef, i, now) 时,转入该函数。它先执行:

dur = orders[oid][2]
et = st + dur
orders[oid][1] = con2(st)
orders[oid].append(con2(et))
orders[oid].append(-1)
idle[chef] = False
insert_work(et, chef)

st 是实际开始时间,et 是结束时间。订单原有 4 个数据项,因此两次 append 分别新增下标 4、5 的字段;下标 1 原先保存到达时间,此时改为实际开始时间。厨师被标记为忙碌,工作记录按结束时间插入 work。

随后执行该厨师订单记录的首尾处理:

if head[chef] == -1:
    head[chef] = oid
else:
    ①
tail[chef] = oid

空表时直接把当前订单设为首结点,每次都把当前订单设为尾结点。结合 head、tail 和新增的 -1 字段,可知程序用 orders 中的记录构造每位厨师的订单链表:head、tail 保存首尾订单的下标,下标 5 的字段用于保存后继订单的下标,-1 表示没有后继。记录扩展后的结构是:

字段下标 0 1 2 3 4 5
含义 订单编号 实际开始时间 所需时间 菜品类型 结束时间 后继订单下标

完成分配后返回接单循环;等待订单后来调用 assign 时,也使用相同的记录和链接方式。

new_work = []
for et, cid in work:
    if et <= now:
        idle[cid] = True
        if wait:
            for k in range(len(wait)):
                woid, wtp, wdur = wait[k]
                chef = find_chef(wtp)
                if chef != -1:
                    assign(chef, woid, now)
                    wait.pop(k)       # pop:弹出索引k处的元素
                    break
    else:
        new_work.append([et, cid])
work = new_work

接收当前时刻已到达的订单后,程序按 work 中的顺序检查结束时间。et <= now 时立即将该厨师标为空闲,并从 wait 的队首向后查找可分配的订单;找到后调用 assign,把订单交给找到的厨师,再用 pop(k) 从等待队列中移除,break 结束本次等待队列扫描。等待更久且当前可以处理的订单因而先被检查。

尚未结束的工作记录进入 new_work,最后用它更新 work。这里释放厨师和给等待订单派单是交替进行的,并未先把本时刻全部结束的厨师统一标为空闲。

③
if i < len(orders):
    now = min(now, con1(orders[i][1]))
if work:
    now = min(now, work[0][0])

完成当前时刻的处理后,空③更新now。其后的两个min分别用下一条订单到达时间、最早工作结束时间限制更新后的时刻,避免越过尚未处理的事件。

整体功能

程序用wait保存等待订单,用work跟踪厨师结束工作的时刻,并为每位厨师建立订单链表。多名同类型厨师同时结束时,固定代码逐个释放厨师后立即派单,可能违背最小厨师编号规则。

例如,两位厨师均擅长川菜,依次收到以下订单:

订单编号 到达时间 所需时间
1 18:00 10分钟
2 18:01 19分钟
3 18:11 9分钟
4 18:15 1分钟

订单1、3交给厨师0,订单2交给厨师1,订单4等待。两人均在18:20结束,但厨师1的结束记录先进入work,程序先释放厨师1并把订单4交给他;题干要求此时交给厨师0。这一调度顺序缺陷位于三处空缺之外。

填空分析

①

执行到 else 时,该厨师已有订单链表。新订单下标为 oid,其后继已初始化为 -1;此时 tail[chef] 仍保存原尾订单下标,下一行才会把表尾改成 oid。因此先把原尾订单的后继指向新订单:

orders[tail[chef]][5] = oid

随后 tail[chef] = oid 更新尾指针,链表就从原尾订单延伸到新订单,原有首结点保持不变。

②

tp 已保存当前订单的菜品类型。题目给出的 find_chef(tp) 用于寻找空闲且擅长该类型的厨师,找到返回厨师编号,找不到返回 -1,正好与后面的 chef != -1 判断衔接。因此填写:

chef = find_chef(tp)

按题干规则,这个省略实现的函数应在符合条件的空闲厨师中选择编号最小者。

③

后续两条min只能限制now,不能使它增大。当剩余事件都在未来时,若不先推进时间,外层循环将一直停留在当前时刻。题目以整数分钟计时,因此填写:

now += 1

这使程序按分钟推进;下一事件仍在当前时刻时,后续min会把now限制回该时刻,继续处理。