LeetCode 1882:使用服务器处理任务
/ 4 min read
Updated:Table of Contents
读题
这类模拟题,最麻烦的是读题:
- 按
tasks的顺序给服务器分配任务,每次选空闲且 weight 最小的服务器;weight 相同时选下标最小的。 - 任务在对应时刻才解锁:
t = 0时第 0 个任务解锁,t = 1时第 1 个任务解锁,依此类推。 - 答案是每个任务被分配到的服务器下标。
思路
看一眼标签就知道要用优先队列。每次取“最小 weight”,本来也能想到小根堆;weight 相同还要比下标,所以堆里存 (weight, index)。
问题归结为两个操作:取服务器和释放服务器。取服务器很简单,把 servers 堆化后依次弹出即可。关键是怎么表示服务器的占用与释放。
初步思路
维护一个全局时间 time,再用一个数组记录每台服务器的占用截止时间:取用时设为 当前时间 + 任务耗时,释放时逐个检查截止时间是否等于当前时间,是的话放回空闲堆。
优化
上面的做法在释放时要遍历所有服务器。但仔细想想,释放时不也是每次取最早的结束时间和当前时间比较吗?
所以“占用中”的服务器同样可以用小根堆表示,每次压入 (占用截止时间, index),释放时只需要看堆顶。
两个细节
- 服务器全部被占用时:让
time直接跳到忙碌堆堆顶,也就是最早的结束时间,省掉一段没有意义的循环。 - 任务解锁:
time相当于一个指针,[0, time]范围内的任务都已经解锁,所以同一时刻可以连续分配多个任务。
实现
代码写得有点丑,凑合看吧:
import heapqfrom typing import List
class Solution: def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: pq, pq_busy, ans = [], [], [] for k, v in enumerate(servers): heapq.heappush(pq, (v, k)) time, pt = 0, 0 while pt < len(tasks): # 释放已到期的服务器 while pq_busy and pq_busy[0][0] == time: _, idx = heapq.heappop(pq_busy) heapq.heappush(pq, (servers[idx], idx)) # 为已解锁的任务分配服务器(pt <= time 即已解锁) while pq and pt <= time and pt < len(tasks): _, idx = heapq.heappop(pq) heapq.heappush(pq_busy, (time + tasks[pt], idx)) ans.append(idx) pt += 1 # 推进时间:服务器全忙时直接跳到最早的结束时刻 time = time + 1 if len(pq_busy) != len(servers) else pq_busy[0][0] return ans整理时只做了等价改写:
(time - pt + 1) > 0写成了更直观的pt <= time,用Go标志控制的释放循环改成了直接判断堆顶。