{x}
blog image

Minimum Rounds to Complete All Tasks

You are given a 0-indexed integer array tasks, where tasks[i] represents the difficulty level of a task. In each round, you can complete either 2 or 3 tasks of the same difficulty level.

Return the minimum rounds required to complete all the tasks, or -1 if it is not possible to complete all the tasks.

 

Example 1:

Input: tasks = [2,2,3,3,2,4,4,4,4,4]
Output: 4
Explanation: To complete all the tasks, a possible plan is:
- In the first round, you complete 3 tasks of difficulty level 2. 
- In the second round, you complete 2 tasks of difficulty level 3. 
- In the third round, you complete 3 tasks of difficulty level 4. 
- In the fourth round, you complete 2 tasks of difficulty level 4.  
It can be shown that all the tasks cannot be completed in fewer than 4 rounds, so the answer is 4.

Example 2:

Input: tasks = [2,3,3]
Output: -1
Explanation: There is only 1 task of difficulty level 2, but in each round, you can only complete either 2 or 3 tasks of the same difficulty level. Hence, you cannot complete all the tasks, and the answer is -1.

 

Constraints:

  • 1 <= tasks.length <= 105
  • 1 <= tasks[i] <= 109

 

Note: This question is the same as 2870: Minimum Number of Operations to Make Array Empty.

Solution Explanation: Minimum Rounds to Complete All Tasks

This problem asks for the minimum number of rounds needed to complete all tasks, given that each round can handle 2 or 3 tasks of the same difficulty level. The solution uses a greedy approach combined with a hash table (or dictionary) for efficient counting.

Approach:

  1. Counting Task Difficulties: First, we count the occurrences of each task difficulty level using a hash table (e.g., Counter in Python, HashMap in Java/C++/Rust, Map in TypeScript). This allows us to process each difficulty level independently.

  2. Checking Feasibility: For each difficulty level, if the count is 1, it's impossible to complete the tasks for that level since we can only do 2 or 3 at a time. In this case, we immediately return -1, indicating that it's not possible to complete all tasks.

  3. Greedy Round Calculation: If the count is greater than 1, we greedily minimize the number of rounds. The most efficient way is to use as many groups of 3 as possible, and then use groups of 2 for any remaining tasks. Mathematically:

    • rounds = count // 3 + (count % 3 != 0)

    This formula does integer division (// or / in most languages) to find the number of groups of 3. If there's a remainder (meaning tasks left over after forming groups of 3), we need an additional round to handle those using groups of 2.

  4. Accumulating Rounds: We accumulate the rounds needed for each difficulty level to get the total minimum number of rounds required to complete all tasks.

Time and Space Complexity:

  • Time Complexity: O(n), where n is the number of tasks. This is because we iterate through the tasks once to count the difficulties, and then iterate through the unique difficulty counts (which is at most n).
  • Space Complexity: O(k), where k is the number of unique task difficulty levels. In the worst case, k could be n (all tasks have different difficulty levels), but it's often much smaller. The space is used to store the counts in the hash table.

Code Examples (Python and Java):

Python:

from collections import Counter
 
class Solution:
    def minimumRounds(self, tasks: List[int]) -> int:
        counts = Counter(tasks)
        total_rounds = 0
        for count in counts.values():
            if count == 1:
                return -1  # Impossible to complete if only one task of a type
            total_rounds += count // 3 + (count % 3 != 0) #Greedy round calculation
        return total_rounds
 

Java:

import java.util.HashMap;
import java.util.Map;
 
class Solution {
    public int minimumRounds(int[] tasks) {
        Map<Integer, Integer> counts = new HashMap<>();
        for (int task : tasks) {
            counts.put(task, counts.getOrDefault(task, 0) + 1);
        }
        int totalRounds = 0;
        for (int count : counts.values()) {
            if (count == 1) {
                return -1; // Impossible
            }
            totalRounds += count / 3 + (count % 3 != 0 ? 1 : 0); //Greedy round calculation
        }
        return totalRounds;
    }
}

The other languages (C++, Go, TypeScript, Rust) follow a very similar structure, using their respective hash table implementations and minor syntax variations. The core logic of counting, checking feasibility, and greedy round calculation remains the same across all the languages.