Maximum Number of Tasks You Can Assign

hard binary search greedy deque

Problem

Task i needs strength tasks[i] to complete, and worker j has strength workers[j]. You also hold pills magical pills: feeding one to a worker adds strength to that worker, and each worker may take at most one pill. Each worker handles at most one task and each task is done by at most one worker, valid only if the (possibly boosted) worker is at least as strong as the task demands. Return the maximum number of tasks that can be completed.

Inputtasks = [3, 2, 1], workers = [0, 3, 3], pills = 1, strength = 1
Output3
Boost worker 0 to strength 1 for task 1; the two strength-3 workers take tasks 2 and 3, so all three tasks get done.

from collections import deque

def max_task_assign(tasks, workers, pills, strength):
    tasks.sort()
    workers.sort()
    n, m = len(tasks), len(workers)

    def can_finish(k):
        free, dq, i = pills, deque(), 0
        for w in workers[m - k:]:            # weakest of the k strongest
            while i < k and tasks[i] <= w + strength:
                dq.append(i)                 # within pill reach
                i += 1
            if not dq:
                return False
            if tasks[dq[0]] <= w:            # easiest task, no pill
                dq.popleft()
            elif free == 0:
                return False
            else:                            # pill on hardest doable
                free -= 1
                dq.pop()
        return True

    lo, hi = 0, min(n, m)
    while lo < hi:                           # binary search max k
        mid = (lo + hi + 1) // 2
        if can_finish(mid):
            lo = mid
        else:
            hi = mid - 1
    return lo
function maxTaskAssign(tasks, workers, pills, strength) {
  tasks.sort((a, b) => a - b);
  workers.sort((a, b) => a - b);
  const n = tasks.length, m = workers.length;
  const canFinish = (k) => {
    let free = pills, i = 0, head = 0;
    const dq = [];                           // deque: head..end
    for (const w of workers.slice(m - k)) {  // weakest of the k strongest
      while (i < k && tasks[i] <= w + strength) dq.push(i++);
      if (head === dq.length) return false;
      if (tasks[dq[head]] <= w) head++;      // easiest task, no pill
      else if (free === 0) return false;
      else { free--; dq.pop(); }             // pill on hardest doable
    }
    return true;
  };
  let lo = 0, hi = Math.min(n, m);
  while (lo < hi) {                          // binary search max k
    const mid = (lo + hi + 1) >> 1;
    if (canFinish(mid)) lo = mid;
    else hi = mid - 1;
  }
  return lo;
}
int maxTaskAssign(int[] tasks, int[] workers, int pills, int strength) {
    Arrays.sort(tasks);
    Arrays.sort(workers);
    int n = tasks.length, m = workers.length;
    int lo = 0, hi = Math.min(n, m);
    while (lo < hi) {                        // binary search max k
        int mid = (lo + hi + 1) >>> 1;
        if (canFinish(tasks, workers, mid, pills, strength)) lo = mid;
        else hi = mid - 1;
    }
    return lo;
}

boolean canFinish(int[] t, int[] w, int k, int free, int strength) {
    Deque<Integer> dq = new ArrayDeque<>();
    int i = 0, m = w.length;
    for (int j = m - k; j < m; j++) {        // weakest of the k strongest
        while (i < k && t[i] <= w[j] + strength) dq.addLast(i++);
        if (dq.isEmpty()) return false;
        if (t[dq.peekFirst()] <= w[j]) dq.pollFirst();  // no pill
        else if (free == 0) return false;
        else { free--; dq.pollLast(); }      // pill on hardest doable
    }
    return true;
}
int maxTaskAssign(vector<int>& tasks, vector<int>& workers, int pills, int strength) {
    sort(tasks.begin(), tasks.end());
    sort(workers.begin(), workers.end());
    int n = tasks.size(), m = workers.size();
    auto canFinish = [&](int k) {
        int free = pills, i = 0;
        deque<int> dq;
        for (int j = m - k; j < m; j++) {    // weakest of the k strongest
            while (i < k && tasks[i] <= workers[j] + strength) dq.push_back(i++);
            if (dq.empty()) return false;
            if (tasks[dq.front()] <= workers[j]) dq.pop_front();  // no pill
            else if (free == 0) return false;
            else { free--; dq.pop_back(); }  // pill on hardest doable
        }
        return true;
    };
    int lo = 0, hi = min(n, m);
    while (lo < hi) {                        // binary search max k
        int mid = (lo + hi + 1) / 2;
        if (canFinish(mid)) lo = mid;
        else hi = mid - 1;
    }
    return lo;
}
Time: O((n + m) log (n + m)) Space: O(min(n, m))