Talentd
FleetCode
Make and follow sheets.Sign in

Minimum Rounds to Complete All Tasks solution and explanation

MediumArrayCountingGreedyHash Table6 min read63.5% acceptanceUpdated 9 Apr 2026
Minimum Rounds to Complete All Tasks : Explanation βž• Live Coding πŸ§‘πŸ»β€πŸ’»πŸ‘©πŸ»β€πŸ’»codestorywithMIK Β· 20:12 Β· 7K viewsWatch on YouTube
9 more videos on this problem
  1. Minimum Rounds to Complete All Tasks : Explanation βž• Live Coding πŸ§‘πŸ»β€πŸ’»πŸ‘©πŸ»β€πŸ’»codestorywithMIK Β· 20:12 Β· 7K views
  2. 2244. Minimum Rounds to Complete All Tasks || Leetcode Weekly Contest 289 || Leetcode 2244Bro Coders Β· 11:40 Β· 5K views
  3. Minimum Rounds to Complete All Tasks | Leetcode 2244 | Maps Math | Contest 289 πŸ”₯πŸ”₯Coding Decoded Β· 12:16 Β· 2K views
  4. MINIMUM ROUNDS TO COMPLETE ALL TASKS | LEETCODE 2244 | PYTHON GREEDY SOLUTIONCracking FAANG Β· 7:40 Β· 1K views
  5. Minimum Rounds to Complete All Tasks | LeetCode 2244 | Theory + Python codeSai Anish Malla Β· 11:00 Β· 826 views
  6. Daily Leetcode #1009: Jan 04, 2023 - 2244. Minimum Rounds to Complete All TasksProgramming Live with Larry Β· 5:44 Β· 779 views
  7. 2244. Minimum Rounds to Complete All Tasks (Leetcode Medium)Programming Live with Larry Β· 12:58 Β· 611 views
  8. 2244. Minimum Rounds to Complete All Tasks | Leetcode 289Mike the Coder Β· 28:59 Β· 457 views
  9. 2 Min | 2244. Minimum Rounds to Complete All Tasks | Contest 289ThinkCode Β· 2:42 Β· 378 views
  10. LeetCode 2244 | Minimum Rounds to Complete All Tasks | Streak πŸ”₯| JAVACoding Spark Β· 16:06 Β· 194 views

The problem

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.

Approach overview

Problem Overview: You receive an array where each value represents the difficulty of a task. In one round you can complete either 2 or 3 tasks of the same difficulty. The goal is to determine the minimum number of rounds required to complete all tasks. If any task difficulty appears only once, finishing all tasks becomes impossible.

The key observation: tasks are independent across difficulty levels. You never mix different difficulties in the same round, so the problem reduces to computing the minimum number of rounds needed for each frequency count.

Approach 1: Divide and Conquer with Frequency Breakdown (O(n) time, O(n) space)

Start by counting occurrences of each difficulty using a hash map from the hash table toolkit. Each frequency becomes a smaller subproblem: how many rounds are needed to finish f identical tasks when each round can remove either 2 or 3 tasks. You recursively reduce the count by prioritizing groups of 3 because they minimize the total rounds. When a remainder of 1 appears (for example f = 4 or f = 7), convert one group of 3 into two groups of 2. If a frequency equals 1, the recursion fails because neither 2 nor 3 tasks can be formed.

This approach conceptually divides the problem by difficulty value and solves each group independently. The array scan and hash counting cost O(n) time, and the map stores at most n unique difficulties, so space complexity is O(n). It clearly illustrates why grouping by three tasks produces the fewest rounds.

Approach 2: Dynamic Programming on Task Frequency (O(n) time, O(n) space)

Another way to think about the problem is as a small dynamic programming recurrence. For a given frequency f, define dp[i] as the minimum rounds needed to finish i tasks. The recurrence becomes dp[i] = 1 + min(dp[i-2], dp[i-3]) because each round removes either 2 or 3 tasks. Precompute values up to the maximum frequency found in the array. If a state becomes unreachable (like i = 1), the difficulty cannot be completed.

Before applying DP, compute frequencies using an array traversal and counting step. Then evaluate the recurrence for each frequency and accumulate the result. The DP table grows only up to the largest frequency, so the total work remains linear relative to the number of tasks.

Recommended for interviews: Interviewers expect the counting + greedy reasoning approach. After building the frequency map, compute rounds using the formula ceil(freq / 3) while handling the freq == 1 edge case. This runs in O(n) time with O(n) space and demonstrates strong intuition about greedy grouping. Explaining the DP formulation still helps show deeper understanding of the underlying recurrence.

Approach 1: Divide and Conquer Approach

This approach involves breaking down the problem into smaller subproblems, solving each of them independently, and combining their results in an efficient way. It often uses recursion to handle each subproblem.

def solve_problem(parameters):
    # Base case solution
    if base_condition:
        return
    # Divide
    mid = calculate_middle
    # Conquer
    solve_problem(left_part)
    solve_problem(right_part)
    # Combine
    # combine left and right results

solve_problem(initial_parameters)
public class Solution {
    public void solveProblem(/* parameters */) {
        // Base case solution
        if (/* base condition */) {
            return;
        }
        // Divide
        int mid = /* calculate middle */;
        // Conquer
        solveProblem(/* left part */);
        solveProblem(/* right part */);
        // Combine
        // combine left and right solutions
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        // Call the method with initial parameters
        sol.solveProblem(/* initial parameters */);
    }
}
#include <iostream>

void solveProblem(/* parameters */) {
    // Base case solution
    if (/* base condition */) {
        return;
    }
    // Divide
    int mid = /* calculate middle */;
    // Conquer
    solveProblem(/* left part */);
    solveProblem(/* right part */);
    // Combine
    // combine left and right solutions
}

int main() {
    // Call the function with initial parameters
    solveProblem(/* initial parameters */);
    return 0;
}
function solveProblem(parameters) {
    // Base case solution
    if (baseCondition) {
        return;
    }
    // Divide
    let mid = calculateMiddle;
    // Conquer
    solveProblem(leftPart);
    solveProblem(rightPart);
    // Combine
    // combine left and right results
}

solveProblem(initialParameters);
#include <stdio.h>

void solveProblem(/* parameters */) {
    // Base case solution
    if (/* base condition */) {
        return;
    }
    // Divide
    int mid = /* calculate middle */;
    // Conquer
    solveProblem(/* left part */);
    solveProblem(/* right part */);
    // Combine
    // combine left and right solutions
}

int main() {
    // Call the function with initial parameters
    solveProblem(/* initial parameters */);
    return 0;
}
class Program {
    static void SolveProblem(/* parameters */) {
        // Base case solution
        if (/* base condition */) {
            return;
        }
        // Divide
        int mid = /* calculate middle */;
        // Conquer
        SolveProblem(/* left part */);
        SolveProblem(/* right part */);
        // Combine
        // combine left and right solutions
    }

    static void Main() {
        SolveProblem(/* initial parameters */);
    }
}

Python's function uses recursion to handle each subproblem after dividing the original problem. It combines results of subproblems to form the solution to the original problem.

Complexity

Time Complexity: O(n log n)
Space Complexity: O(log n)

Try this approach in the editor

Approach 2: Dynamic Programming Approach

This approach involves solving complex problems by breaking them into simpler overlapping subproblems, storing the results of subproblems to avoid redundant calculations, and constructing a solution from these stored results.

dp = [-1] * 100  # Assume maximum 'n' is 100

def solve_problem(n):
    if n == 0 or n == 1:
        return n
    if dp[n] != -1:
        return dp[n]
    dp[n] = solve_problem(n - 1) + solve_problem(n - 2)
    return dp[n]

n = 10  # Example parameter
print(solve_problem(n))
import java.util.Arrays;

public class Solution {
    static int[] dp = new int[100]; // Initialize with a large enough size

    public static int solveProblem(int n) {
        if (n == 0 || n == 1) return n;
        if (dp[n] != -1) return dp[n];
        dp[n] = solveProblem(n - 1) + solveProblem(n - 2);
        return dp[n];
    }

    public static void main(String[] args) {
        Arrays.fill(dp, -1); // Fill the dp array with -1
        int n = 10;
        System.out.println(solveProblem(n));
    }
}
#include <iostream>
#include <vector>

std::vector<int> dp(100, -1); // Initialize dp array

int solveProblem(int n) {
    if (n == 0 || n == 1) return n;
    if (dp[n] != -1) return dp[n];
    dp[n] = solveProblem(n-1) + solveProblem(n-2);
    return dp[n];
}

int main() {
    int n = 10;
    std::cout << solveProblem(n) << std::endl;
    return 0;
}
let dp = new Array(100).fill(-1);

function solveProblem(n) {
    if (n === 0 || n === 1) return n;
    if (dp[n] !== -1) return dp[n];
    dp[n] = solveProblem(n - 1) + solveProblem(n - 2);
    return dp[n];
}

let n = 10;
console.log(solveProblem(n));
#include <stdio.h>

int dp[100]; // Assuming maximum 'n' is 100 for simplicity

int solveProblem(int n) {
    if (n == 0 || n == 1) return n; // Base cases
    if (dp[n] != -1) return dp[n]; // Check if already computed
    dp[n] = solveProblem(n-1) + solveProblem(n-2);
    return dp[n];
}

int main() {
    int n = 10; // Example parameter
    for (int i = 0; i <= n; i++) dp[i] = -1; // Initialize the dp array
    printf("%d\n", solveProblem(n));
    return 0;
}
using System;

class Program {
    static int[] dp = new int[100]; // Assume maximum 'n' is 100

    static int SolveProblem(int n) {
        if (n == 0 || n == 1) return n;
        if (dp[n] != -1) return dp[n];
        dp[n] = SolveProblem(n - 1) + SolveProblem(n - 2);
        return dp[n];
    }

    static void Main() {
        Array.Fill(dp, -1); // Fill the array with -1
        int n = 10;
        Console.WriteLine(SolveProblem(n));
    }
}

Python solution using a list to store the values of subproblems. It recursively calculates and stores results for use in overlapping subproblems.

Complexity

Time Complexity: O(n)
Space Complexity: O(n)

Try this approach in the editor

Approach 3: Hash Table

We use a hash table to count the number of tasks for each difficulty level. Then we traverse the hash table. For each difficulty level, if the number of tasks is 1, then it is impossible to complete all tasks, so we return -1. Otherwise, we calculate the number of rounds needed to complete tasks of this difficulty level and add it to the answer.

Finally, we return the answer.

The time complexity is O(n), and the space complexity is O(n), where n is the length of the tasks array.

function minimumRounds(tasks: number[]): number {
    const cnt = new Map();
    for (const t of tasks) {
        cnt.set(t, (cnt.get(t) || 0) + 1);
    }
    let ans = 0;
    for (const v of cnt.values()) {
        if (v == 1) {
            return -1;
        }
        ans += Math.floor(v / 3) + (v % 3 === 0 ? 0 : 1);
    }
    return ans;
}
func minimumRounds(tasks []int) int {
	cnt := map[int]int{}
	for _, t := range tasks {
		cnt[t]++
	}
	ans := 0
	for _, v := range cnt {
		if v == 1 {
			return -1
		}
		ans += v / 3
		if v%3 != 0 {
			ans++
		}
	}
	return ans
}
use std::collections::HashMap;

impl Solution {
    pub fn minimum_rounds(tasks: Vec<i32>) -> i32 {
        let mut cnt = HashMap::new();
        for &t in tasks.iter() {
            let count = cnt.entry(t).or_insert(0);
            *count += 1;
        }
        let mut ans = 0;
        for &v in cnt.values() {
            if v == 1 {
                return -1;
            }
            ans += v / 3 + (if v % 3 == 0 { 0 } else { 1 });
        }
        ans
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Divide and Conquer Approach

Time Complexity: O(n log n)
Space Complexity: O(log n)

Dynamic Programming Approach

Time Complexity: O(n)
Space Complexity: O(n)

Hash Table–

Complexity analysis

ApproachTimeSpaceWhen to Use
Divide and Conquer with Frequency CountingO(n)O(n)General case. Count tasks and greedily split frequencies into groups of 3 and 2.
Dynamic Programming on FrequencyO(n)O(n)Useful when modeling the problem as a recurrence or demonstrating DP reasoning in interviews.

Frequently asked questions

Is Minimum Rounds to Complete All Tasks easy or hard?

Minimum Rounds to Complete All Tasks is rated Medium difficulty on LeetCode. The implementation is short, but recognizing the greedy grouping rule and handling the remainder case correctly requires careful reasoning about frequencies.

Minimum Rounds to Complete All Tasks Python/Java solution

Python and Java implementations typically use a dictionary or HashMap to count task frequencies. After counting, iterate through the map values and compute rounds using ceil(freq / 3) while handling the freq == 1 edge case. Both implementations run in O(n) time with O(n) extra space.

How to solve Minimum Rounds to Complete All Tasks in O(n)?

Traverse the array and store frequencies in a hash map. For each difficulty count f, check if f equals 1; if so return -1. Otherwise compute the required rounds using ceil(f / 3) and add it to the total. This works because using as many groups of 3 as possible minimizes the number of rounds.

What is the best approach for Minimum Rounds to Complete All Tasks?

The best approach uses greedy counting with a hash map. First count how many times each difficulty appears. For a frequency f, the minimum rounds equals ceil(f / 3) because groups of 3 minimize the number of rounds. If any difficulty appears exactly once, the task set cannot be completed and the answer is -1.

Is Minimum Rounds to Complete All Tasks asked at Google/Amazon/Meta?

Problems involving frequency counting and greedy grouping appear frequently in interviews at companies like Amazon, Google, and Meta. The exact LeetCode problem may not always appear, but similar questions testing hash maps, greedy reasoning, and counting strategies are common.

What data structure is used in Minimum Rounds to Complete All Tasks?

A hash table (hash map) is the primary data structure. It stores the frequency of each task difficulty so you can process each group independently. This allows constant-time updates while scanning the array and enables the greedy round calculation.

What is the time complexity of Minimum Rounds to Complete All Tasks?

The optimal solution runs in O(n) time where n is the number of tasks. The algorithm scans the array once to build a frequency map and then processes each unique difficulty in constant time. Space complexity is O(n) in the worst case when all tasks have different difficulties.

Comments

Now write it yourselfClose this page and solve Minimum Rounds to Complete All Tasks in the editor, without looking.
Practise Minimum Rounds to Complete All Tasks