You are given two arrays: servers
and tasks
.
servers[i]
is the weight (processing power) of the i-th server. Servers are indexed from 0.tasks[j]
is the time required to process the j-th task.ans
where ans[j]
is the index of the server that processes the j-th task.
tasks[0]
at time 0, tasks[1]
at time 1, ...).
At first glance, a brute-force approach would be to check every server for every task at each time step, assigning the task to the first free server with the smallest weight and index. However, this would be inefficient for large inputs.
To optimize, we need to efficiently track which servers are free and which are busy, always being able to quickly get the next available server with the lowest weight and index. This suggests using a data structure like a min-heap (priority queue) for free servers. Additionally, we need to know when busy servers will become free, so we can use another heap to track busy servers by their release time.
The key is to simulate the passage of time, processing tasks in order, and always assigning them to the optimal available server.
Let's break down the solution step by step:
j
), first move any servers that have become free by time j
from the busy heap back to the free heap.
This approach ensures that we always assign tasks to the optimal server and efficiently manage the state of each server as tasks are processed.
Input:
servers = [3,3,2]
, tasks = [1,2,3,2,1,2]
Step-by-step:
[2,2,0,2,1,2]
Brute-force approach:
For each task, check every server for availability, resulting in O(n * m) time where n is the number of servers and m is the number of tasks. This is inefficient for large inputs.
Optimized approach (using heaps):
The problem requires efficiently assigning tasks to servers based on their availability and processing power. By using two min-heaps—one for free servers and one for busy servers—we can always assign each task to the optimal server in logarithmic time. This approach is both elegant and efficient, making it well-suited for large-scale inputs and real-world scheduling problems.
import heapq
class Solution:
def assignTasks(self, servers, tasks):
n = len(servers)
m = len(tasks)
free = []
busy = []
for i, w in enumerate(servers):
heapq.heappush(free, (w, i))
ans = [0] * m
time = 0
for j, duration in enumerate(tasks):
time = max(time, j)
# Release servers that are done by current time
while busy and busy[0][0] <= time:
_, w, i = heapq.heappop(busy)
heapq.heappush(free, (w, i))
# If no free servers, fast-forward time to next server available
if not free:
time = busy[0][0]
while busy and busy[0][0] == time:
_, w, i = heapq.heappop(busy)
heapq.heappush(free, (w, i))
w, i = heapq.heappop(free)
ans[j] = i
heapq.heappush(busy, (time + duration, w, i))
return ans
#include <vector>
#include <queue>
using namespace std;
class Solution {
public:
vector<int> assignTasks(vector<int>& servers, vector<int>& tasks) {
int n = servers.size(), m = tasks.size();
// min-heap for free servers: (weight, index)
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> free;
// min-heap for busy servers: (release time, weight, index)
priority_queue<tuple<long long, int, int>, vector<tuple<long long, int, int>>, greater<tuple<long long, int, int>>> busy;
for (int i = 0; i < n; ++i)
free.push({servers[i], i});
vector<int> ans(m);
long long time = 0;
for (int j = 0; j < m; ++j) {
time = max(time, (long long)j);
while (!busy.empty() && get<0>(busy.top()) <= time) {
auto [t, w, i] = busy.top(); busy.pop();
free.push({w, i});
}
if (free.empty()) {
time = get<0>(busy.top());
while (!busy.empty() && get<0>(busy.top()) == time) {
auto [t, w, i] = busy.top(); busy.pop();
free.push({w, i});
}
}
auto [w, i] = free.top(); free.pop();
ans[j] = i;
busy.push({time + tasks[j], w, i});
}
return ans;
}
};
import java.util.*;
class Solution {
public int[] assignTasks(int[] servers, int[] tasks) {
int n = servers.length, m = tasks.length;
PriorityQueue<int[]> free = new PriorityQueue<>((a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
PriorityQueue<long[]> busy = new PriorityQueue<>((a, b) -> a[0] != b[0] ? Long.compare(a[0], b[0]) : (a[1] == b[1] ? (int)a[2] - (int)b[2] : (int)a[1] - (int)b[1]));
for (int i = 0; i < n; ++i)
free.offer(new int[]{servers[i], i});
int[] ans = new int[m];
long time = 0;
for (int j = 0; j < m; ++j) {
time = Math.max(time, j);
while (!busy.isEmpty() && busy.peek()[0] <= time) {
long[] b = busy.poll();
free.offer(new int[]{(int)b[1], (int)b[2]});
}
if (free.isEmpty()) {
time = busy.peek()[0];
while (!busy.isEmpty() && busy.peek()[0] == time) {
long[] b = busy.poll();
free.offer(new int[]{(int)b[1], (int)b[2]});
}
}
int[] f = free.poll();
ans[j] = f[1];
busy.offer(new long[]{time + tasks[j], f[0], f[1]});
}
return ans;
}
}
class Heap {
constructor(compare) {
this.data = [];
this.compare = compare;
}
push(item) {
this.data.push(item);
this._siftUp();
}
pop() {
if (this.size() === 1) return this.data.pop();
const top = this.data[0];
this.data[0] = this.data.pop();
this._siftDown();
return top;
}
peek() {
return this.data[0];
}
size() {
return this.data.length;
}
_siftUp() {
let idx = this.data.length - 1;
while (idx > 0) {
let p = Math.floor((idx - 1) / 2);
if (this.compare(this.data[idx], this.data[p]) < 0) {
[this.data[idx], this.data[p]] = [this.data[p], this.data[idx]];
idx = p;
} else break;
}
}
_siftDown() {
let idx = 0, n = this.data.length;
while (2 * idx + 1 < n) {
let l = 2 * idx + 1, r = 2 * idx + 2, min = l;
if (r < n && this.compare(this.data[r], this.data[l]) < 0) min = r;
if (this.compare(this.data[min], this.data[idx]) < 0) {
[this.data[idx], this.data[min]] = [this.data[min], this.data[idx]];
idx = min;
} else break;
}
}
}
var assignTasks = function(servers, tasks) {
let n = servers.length, m = tasks.length;
let free = new Heap((a, b) => a[0] - b[0] || a[1] - b[1]);
let busy = new Heap((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
for (let i = 0; i < n; ++i) free.push([servers[i], i]);
let ans = Array(m), time = 0;
for (let j = 0; j < m; ++j) {
time = Math.max(time, j);
while (busy.size() && busy.peek()[0] <= time) {
let [t, w, i] = busy.pop();
free.push([w, i]);
}
if (free.size() === 0) {
time = busy.peek()[0];
while (busy.size() && busy.peek()[0] === time) {
let [t, w, i] = busy.pop();
free.push([w, i]);
}
}
let [w, i] = free.pop();
ans[j] = i;
busy.push([time + tasks[j], w, i]);
}
return ans;
};