Talentd
FleetCode
Make and follow sheets.Sign in

Maximum Number of Tasks You Can Assign solution and explanation

HardArrayBinary SearchGreedyMonotonic Queue7 min read50.1% acceptanceUpdated 9 Apr 2026
Maximum Number of Tasks You Can Assign | Detailed Explanation | Leetcode 2071 | codestorywithMIKcodestorywithMIK · 47:11 · 13K viewsWatch on YouTube
9 more videos on this problem
  1. Maximum Number of Tasks You Can Assign | Detailed Explanation | Leetcode 2071 | codestorywithMIKcodestorywithMIK · 47:11 · 13K views
  2. Maximum Number of Tasks You Can Assign | Leetcode 2071Techdose · 39:46 · 7K views
  3. 2071. Maximum Number of Tasks You Can Assign | Leetcode Daily - PythonLeetcode Daily · 8:37 · 2K views
  4. 2071. Maximum Number of Tasks You Can Assign (Leetcode Hard)Programming Live with Larry · 1:56:41 · 1K views
  5. 【每日一题】LeetCode 2071. Maximum Number of Tasks You Can AssignHuifeng Guan · 25:22 · 1K views
  6. Daily Leetcode #1857: May 01, 2025 - 2071. Maximum Number of Tasks You Can AssignProgramming Live with Larry · 36:16 · 1K views
  7. Leetcode 2071. Maximum Number of Tasks You Can Assign | Binary Search + Greedy + Deque | PythonStatLearn Tech · 17:45 · 1K views
  8. 2071. Maximum Number of Tasks You Can Assign | Leetcode Daily 1 May 2025 | Java | HindiAlgorithmHQ · 18:06 · 364 views
  9. Leetcode 2071 | Maximum Number Of Tasks You Can Assign | Leetcode Daily QuestionCoding Grower · 14:05 · 273 views
  10. Maximum Number of Tasks You Can Assign - Leetcode 2071ThinkOutsideTheBox · 21:34 · 255 views

The problem

You have n tasks and m workers. Each task has a strength requirement stored in a 0-indexed integer array tasks, with the ith task requiring tasks[i] strength to complete. The strength of each worker is stored in a 0-indexed integer array workers, with the jth worker having workers[j] strength. Each worker can only be assigned to a single task and must have a strength greater than or equal to the task's strength requirement (i.e., workers[j] >= tasks[i]).

Additionally, you have pills magical pills that will increase a worker's strength by strength. You can decide which workers receive the magical pills, however, you may only give each worker at most one magical pill.

Given the 0-indexed integer arrays tasks and workers and the integers pills and strength, return the maximum number of tasks that can be completed.

Example 1:

Input: tasks = [3,2,1], workers = [0,3,3], pills = 1, strength = 1
Output: 3
Explanation:
We can assign the magical pill and tasks as follows:
- Give the magical pill to worker 0.
- Assign worker 0 to task 2 (0 + 1 >= 1)
- Assign worker 1 to task 1 (3 >= 2)
- Assign worker 2 to task 0 (3 >= 3)

Example 2:

Input: tasks = [5,4], workers = [0,0,0], pills = 1, strength = 5
Output: 1
Explanation:
We can assign the magical pill and tasks as follows:
- Give the magical pill to worker 0.
- Assign worker 0 to task 0 (0 + 5 >= 5)

Example 3:

Input: tasks = [10,15,30], workers = [0,10,10,10,10], pills = 3, strength = 10
Output: 2
Explanation:
We can assign the magical pills and tasks as follows:
- Give the magical pill to worker 0 and worker 1.
- Assign worker 0 to task 0 (0 + 10 >= 10)
- Assign worker 1 to task 1 (10 + 10 >= 15)
The last pill is not given because it will not make any worker strong enough for the last task.

Constraints:

  • n == tasks.length
  • m == workers.length
  • 1 <= n, m <= 5 * 104
  • 0 <= pills <= m
  • 0 <= tasks[i], workers[j], strength <= 109

Approach overview

Problem Overview: You receive two arrays: tasks representing required strength and workers representing worker strength. A worker can complete a task if their strength meets the requirement. You also have a limited number of pills that temporarily increase a worker's strength by a fixed amount. The goal is to assign tasks to workers so that the number of completed tasks is maximized.

Approach 1: HashMap + Greedy Feasibility Check (Binary Search) (Time: O(n log n), Space: O(n))

Sort both tasks and workers. Then run a binary search on the number of tasks you attempt to assign. For each candidate value k, try assigning the k easiest tasks to the k strongest workers. A greedy strategy checks from the hardest of these tasks downward. If a worker cannot meet the requirement directly, a pill can be used to temporarily boost their strength. A HashMap or counting structure tracks available boosted assignments and ensures pills are consumed only when necessary. The feasibility check runs in linear time per binary search step, making the full algorithm efficient.

Approach 2: Balanced BST for Ordered Key Access (Binary Search + Greedy) (Time: O(n log n), Space: O(n))

This approach also sorts the arrays and binary searches the number of assignable tasks. During the feasibility check, store the strongest available workers in a balanced binary search tree (such as TreeMap, multiset, or SortedList). Iterate tasks from hardest to easiest among the selected subset. First try to match a worker whose strength already satisfies the task. If none exists, search the structure for the weakest worker who can complete the task after taking a pill. Ordered lookup from the BST makes these operations O(log n). This approach is clean and reliable when implementing greedy matching with dynamic worker availability.

Both solutions rely on the same key insight: the answer is monotonic. If you can assign k tasks, you can assign any number less than k. That property enables efficient binary search. The greedy assignment ensures pills are used only when required and always on the weakest possible worker who can still finish the job.

Recommended for interviews: The binary search + greedy feasibility check using ordered structures is what interviewers typically expect. It demonstrates understanding of monotonic search spaces, efficient task matching, and careful resource usage. Being comfortable with sorted arrays and structures like greedy algorithms, sorting, and ordered containers makes the solution straightforward to reason about.

Approach 1: Approach 1: Use a HashMap

The first approach involves using a hashmap to store and quickly access data. This is useful when you need to perform quick insertion, deletion, or access operations. You can leverage the hashmap to map keys to their respective values, which provides average O(1) time complexity for these operations.

hash_map = {}
hash_map[1] = 10
hash_map[2] = 20
print(f'Key: 1, Value: {hash_map.get(1)}')
print(f'Key: 3, Value: {hash_map.get(3, "Not Found")})'
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, 10);
        hashMap.put(2, 20);
        System.out.println("Key: 1, Value: " + hashMap.get(1));
        System.out.println("Key: 3, Value: " + hashMap.getOrDefault(3, -1));
    }
}
#include <iostream>
#include <unordered_map>

int main() {
  std::unordered_map<int, int> hashMap;
  hashMap[1] = 10;
  hashMap[2] = 20;
  std::cout << "Key: 1, Value: " << hashMap[1] << std::endl;
  if (hashMap.find(3) != hashMap.end()) {
    std::cout << "Key: 3, Value: " << hashMap[3] << std::endl;
  } else {
    std::cout << "Key: 3, Value: Not Found" << std::endl;
  }
  return 0;
}
let hashMap = new Map();
hashMap.set(1, 10);
hashMap.set(2, 20);
console.log(`Key: 1, Value: ${hashMap.get(1)}`);
console.log(`Key: 3, Value: ${hashMap.has(3) ? hashMap.get(3) : 'Not Found'}`);
#include <stdio.h>
#include <stdlib.h>

#define TABLE_SIZE 100

// Structure of each item in the hashmap
struct item {
  int key;
  int value;
};

// Hash function
int hashFunction(int key) {
  return key % TABLE_SIZE;
}

// Function to insert a key-value pair into the hashmap
void insert(struct item* table[], int key, int value) {
  int index = hashFunction(key);
  table[index] = malloc(sizeof(struct item));
  table[index]->key = key;
  table[index]->value = value;
}

// Function to search for a value by key
int search(struct item* table[], int key) {
  int index = hashFunction(key);
  if (table[index] != NULL && table[index]->key == key) {
    return table[index]->value;
  }
  return -1; // Indicates not found
}

int main() {
  struct item* table[TABLE_SIZE] = {NULL};
  insert(table, 1, 10);
  insert(table, 2, 20);
  printf("Key: 1, Value: %d\n", search(table, 1));
  printf("Key: 3, Value: %d\n", search(table, 3));
  return 0;
}
using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        Dictionary<int, int> hashMap = new Dictionary<int, int>();
        hashMap[1] = 10;
        hashMap[2] = 20;
        Console.WriteLine("Key: 1, Value: " + hashMap[1]);
        Console.WriteLine("Key: 3, Value: " + (hashMap.ContainsKey(3) ? hashMap[3].ToString() : "Not Found"));
    }
}

This Python implementation uses a dictionary to achieve hashmap functionality, taking advantage of the average O(1) time complexity of Python's underlying dictionary operations. get is used to provide default values for non-existent keys.

Complexity

The time complexity for inserting and retrieving values using a dictionary is O(1) on average, with a space complexity of O(n) where n is the number of elements stored in the dictionary.

Try this approach in the editor

Approach 2: Approach 2: Balanced BST for Ordered Key Access

The second approach involves using a Balanced Binary Search Tree (BST), such as an AVL tree or Red-Black tree, which organizes keys in sorted order, supporting in-order traversal and ordered operations. This is beneficial when operations requiring order (like finding the smallest/largest element) are needed alongside standard operations.

from sortedcontainers import SortedDict

tree_map = SortedDict()
tree_map[10] = 100
tree_map[20] = 200
print(f'Key: 10, Value: {tree_map[10]}')
del tree_map[10]
print(tree_map)
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        TreeMap<Integer, Integer> treeMap = new TreeMap<>();
        treeMap.put(10, 100);
        treeMap.put(20, 200);
        System.out.println("Key: 10, Value: " + treeMap.get(10));
        treeMap.remove(10);
        System.out.println("TreeMap after removal: " + treeMap);
    }
}
#include <iostream>
#include <set>

int main() {
    std::set<int> bst;
    bst.insert(10);
    bst.insert(20);
    std::cout << "20 found: " << (bst.find(20) != bst.end()) << std::endl;
    bst.erase(10);
    return 0;
}
// JavaScript does not have a native balanced tree structure.
// This example uses sorted key access through an array workaround.

let treeMap = new Map();
treeMap.set(10, 100);
treeMap.set(20, 200);

console.log(`Key: 10, Value: ${treeMap.get(10)}`);
treeMap.delete(10);
console.log('Map after removal:', Array.from(treeMap).sort((a,b) => a[0] - b[0]));
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int key, value;
    struct Node* left, * right;
    int height;
};

int height(struct Node* node) {
    return (node == NULL) ? 0 : node->height;
}

// Auxiliary functions for AVL rebalancing (rotations)
// And typical AVL operations like inserting or balancing.
// For complete AVL implementation, resource usage exceeds the fit here.

int main() {
    // Example placeholder to demonstrate implementation.
    struct Node* root = NULL;
    // Use AVL insert & other functionalities here
    return 0;
}
using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        SortedDictionary<int, int> treeMap = new SortedDictionary<int, int>();
        treeMap[10] = 100;
        treeMap[20] = 200;
        Console.WriteLine("Key: 10, Value: " + treeMap[10]);
        treeMap.Remove(10);
        Console.WriteLine("SortedDictionary after removal:");
        foreach (var kvp in treeMap) {
            Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
        }
    }
}

This Python approach uses the SortedDict from the sortedcontainers module, providing a balanced BST-like structure that keeps keys ordered while enabling efficient management of ordered dictionary features.

Complexity

The SortedDict supports operations like insertion, deletion, and access in O(log n) time complexity. The space complexity is O(n), with n as the number of elements.

Try this approach in the editor

Approach 3: Greedy + Binary Search

Sort the tasks in ascending order of completion time and the workers in ascending order of ability.

Suppose the number of tasks we want to assign is x. We can greedily assign the first x tasks to the x workers with the highest strength. If it is possible to complete x tasks, then it is also possible to complete x-1, x-2, x-3, ..., 1, 0 tasks. Therefore, we can use binary search to find the maximum x such that it is possible to complete x tasks.

We define a function check(x) to determine whether it is possible to complete x tasks.

The implementation of check(x) is as follows:

Iterate through the x workers with the highest strength in ascending order. Let the current worker being processed be j. The current available tasks must satisfy tasks[i] leq workers[j] + strength.

If the smallest required strength task task[i] among the current available tasks is less than or equal to workers[j], then worker j can complete task task[i] without using a pill. Otherwise, the current worker must use a pill. If there are pills remaining, use one pill and complete the task with the highest required strength among the current available tasks. Otherwise, return false.

The time complexity is O(n times log n), and the space complexity is O(n), where n is the number of tasks.

function maxTaskAssign(
    tasks: number[],
    workers: number[],
    pills: number,
    strength: number,
): number {
    tasks.sort((a, b) => a - b);
    workers.sort((a, b) => a - b);

    const n = tasks.length;
    const m = workers.length;

    const check = (x: number): boolean => {
        const dq = new Array<number>(x);
        let head = 0;
        let tail = 0;
        const empty = () => head === tail;
        const pushBack = (val: number) => {
            dq[tail++] = val;
        };
        const popFront = () => {
            head++;
        };
        const popBack = () => {
            tail--;
        };
        const front = () => dq[head];

        let i = 0;
        let p = pills;

        for (let j = m - x; j < m; j++) {
            while (i < x && tasks[i] <= workers[j] + strength) {
                pushBack(tasks[i]);
                i++;
            }

            if (empty()) return false;

            if (front() <= workers[j]) {
                popFront();
            } else {
                if (p === 0) return false;
                p--;
                popBack();
            }
        }
        return true;
    };

    let [left, right] = [0, Math.min(n, m)];
    while (left < right) {
        const mid = (left + right + 1) >> 1;
        if (check(mid)) left = mid;
        else right = mid - 1;
    }
    return left;
}
func maxTaskAssign(tasks []int, workers []int, pills int, strength int) int {
	sort.Ints(tasks)
	sort.Ints(workers)
	n, m := len(tasks), len(workers)
	left, right := 0, min(m, n)
	check := func(x int) bool {
		p := pills
		q := []int{}
		i := 0
		for j := m - x; j < m; j++ {
			for i < x && tasks[i] <= workers[j]+strength {
				q = append(q, tasks[i])
				i++
			}
			if len(q) == 0 {
				return false
			}
			if q[0] <= workers[j] {
				q = q[1:]
			} else if p == 0 {
				return false
			} else {
				p--
				q = q[:len(q)-1]
			}
		}
		return true
	}
	for left < right {
		mid := (left + right + 1) >> 1
		if check(mid) {
			left = mid
		} else {
			right = mid - 1
		}
	}
	return left
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Use a HashMap

The time complexity for inserting and retrieving values using a dictionary is O(1) on average, with a space complexity of O(n) where n is the number of elements stored in the dictionary.

Approach 2: Balanced BST for Ordered Key Access

The SortedDict supports operations like insertion, deletion, and access in O(log n) time complexity. The space complexity is O(n), with n as the number of elements.

Greedy + Binary Search–

Complexity analysis

ApproachTimeSpaceWhen to Use
HashMap + Greedy Feasibility Check (Binary Search)O(n log n)O(n)Good general solution when using arrays and counting structures to simulate worker assignment.
Balanced BST for Ordered Key AccessO(n log n)O(n)Preferred when language provides ordered sets/maps for efficient worker lookup.

Frequently asked questions

Is Maximum Number of Tasks You Can Assign easy or hard?

Maximum Number of Tasks You Can Assign is classified as a Hard problem on LeetCode. It combines binary search on the answer with greedy task assignment and careful use of ordered data structures.

Maximum Number of Tasks You Can Assign Python/Java solution

Python solutions usually rely on sorting plus structures like deque or SortedList for worker assignment during the binary search check. Java implementations commonly use TreeMap or priority queues to locate the weakest worker who can complete each task.

How to solve Maximum Number of Tasks You Can Assign in O(n)?

A full O(n) solution is generally not achievable because sorting or ordered lookups are required to match tasks and workers efficiently. Most optimal implementations run in O(n log n) due to sorting and balanced tree operations used during the greedy feasibility check.

What is the best approach for Maximum Number of Tasks You Can Assign?

The best approach uses binary search combined with a greedy feasibility check. After sorting tasks and workers, binary search determines how many tasks might be possible. For each candidate value, a greedy assignment using ordered structures verifies whether workers and pills can complete those tasks in O(n) or O(n log n) time.

Is Maximum Number of Tasks You Can Assign asked at Google/Amazon/Meta?

Problems involving greedy matching, binary search on the answer, and resource allocation frequently appear in interviews at companies like Google, Amazon, and Meta. Variants of this task‑assignment pattern are common in senior algorithm interviews.

What data structure is used in Maximum Number of Tasks You Can Assign?

Typical solutions use sorted arrays combined with ordered data structures such as a balanced BST (TreeMap, multiset, or SortedList). Some implementations also use deques or hash-based structures during the greedy feasibility check.

What is the time complexity of Maximum Number of Tasks You Can Assign?

The optimal solution runs in O(n log n) time. Sorting the tasks and workers takes O(n log n), and binary search over the number of tasks performs feasibility checks that rely on ordered lookups or greedy scans.

Comments

Now write it yourselfClose this page and solve Maximum Number of Tasks You Can Assign in the editor, without looking.
Practise Maximum Number of Tasks You Can Assign