Process Tasks Using Servers
Problem
i has weight servers[i]; task j needs
tasks[j] seconds and joins a FIFO queue at second j. Whenever a
server is idle and the queue is non-empty, the front task goes to the idle server with the
smallest weight (ties: smallest index). If none is idle, tasks wait until one
frees up. Return ans where ans[j] is the server that ran task
j.servers = [3,3,2], tasks = [1,2,3,2,1,2]Output[2,2,0,2,1,2]servers = [5,1,4,3,2], tasks = [2,1,2,4,5,2,1]Output[1,4,1,4,1,3,2]1 ≤ n, m ≤ 2·105 · 1 ≤ servers[i], tasks[j] ≤ 2·105 · finish times can exceed 231
Try it yourself
class Solution {
public int[] assignTasks(int[] servers, int[] tasks) {
}
}
Instinct "K largest / smallest / closest / most frequent", or "always take the best available next"? Keep a heap - size k for top-K, or a ready-queue for scheduling.
Memory hack Guard the top-K club with a bouncer - a min-heap of size k whose root is the weakest member; anyone stronger kicks them out.
Open the Heap & Top-K guide →Approaches
Brute ForceLinear Scan of All Servers per Task
O(m · n)Space O(n)Track freeAt[i] for each server. For task j, the clock
is max(clock, j); scan all servers for the lightest one with
freeAt ≤ clock. If none, set the clock to the minimum
freeAt and scan again. Tasks start in queue order, so the clock never
moves backwards.
public int[] assignTasks(int[] servers, int[] tasks) { int n = servers.length, m = tasks.length; long[] freeAt = new long[n]; int[] ans = new int[m]; long clock = 0; for (int j = 0; j < m; j++) { clock = Math.max(clock, j); int pick = choose(servers, freeAt, clock); if (pick == -1) { // everyone busy: wait for the earliest finisher long next = Long.MAX_VALUE; for (long f : freeAt) next = Math.min(next, f); clock = next; pick = choose(servers, freeAt, clock); } ans[j] = pick; freeAt[pick] = clock + tasks[j]; } return ans; } private int choose(int[] servers, long[] freeAt, long clock) { int pick = -1; for (int i = 0; i < servers.length; i++) { // strict < keeps the smaller index on equal weight if (freeAt[i] <= clock && (pick == -1 || servers[i] < servers[pick])) pick = i; } return pick; }
Obviously faithful to the rules, but 4·1010 steps at the limits.
OptimalIdle Heap + Busy Heap with Clock Jumps
O((n + m) log n)Space O(n)The idle heap answers “lightest free server” in O(log n); the busy heap answers “who frees up next” in O(log n). Each assignment releases finished servers, jumps the clock only when nobody is idle, then moves one server from idle to busy. Every server move is a heap operation, so the whole run is O((n + m) log n).
public int[] assignTasks(int[] servers, int[] tasks) { // idle: {weight, index} busy: {finishTime, index} PriorityQueue<int[]> idle = new PriorityQueue<>((a, b) -> a[0] != b[0] ? Integer.compare(a[0], b[0]) : Integer.compare(a[1], b[1])); PriorityQueue<long[]> busy = new PriorityQueue<>((a, b) -> Long.compare(a[0], b[0])); for (int i = 0; i < servers.length; i++) idle.add(new int[]{servers[i], i}); int[] ans = new int[tasks.length]; long clock = 0; for (int j = 0; j < tasks.length; j++) { clock = Math.max(clock, j); if (idle.isEmpty()) clock = Math.max(clock, busy.peek()[0]); while (!busy.isEmpty() && busy.peek()[0] <= clock) { int idx = (int) busy.poll()[1]; idle.add(new int[]{servers[idx], idx}); } int[] s = idle.poll(); ans[j] = s[1]; busy.add(new long[]{clock + tasks[j], s[1]}); } return ans; }
Handles 2·105 servers and tasks easily; the standard answer.
finish ≤ clock before polling idle
— several may free at the same second and the lightest must win.At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Linear Scan of All Servers per Task | O(m · n) | O(n) |
| Optimal | Idle Heap + Busy Heap with Clock Jumps | O((n + m) log n) | O(n) |
- Using
intfor finish times: 2·105 tasks × 2·105 s overflows. - Starting task
jat secondjeven when it had to queue — the clock must be monotone. - Releasing only one finished server per step, so a lighter one finishing at the same time is ignored.
- Clarify the queue semantics: tasks are strictly FIFO, a waiting task blocks the ones behind it.
- Name both heaps and their orderings before writing comparators.
- Point out the clock-jump: never tick second by second, the times can be ~1010.