Facebook Pixel

3190. Find Minimum Operations to Make All Elements Divisible by Three

Problem Description

You have an integer array nums. In each operation, you can either add 1 to any element or subtract 1 from any element in the array.

Your goal is to find the minimum total number of operations needed to make every element in nums divisible by 3.

For example, if you have an element with value 5, you could either:

  • Subtract 2 from it to get 3 (which is divisible by 3) - this takes 2 operations
  • Add 1 to it to get 6 (which is divisible by 3) - this takes 1 operation

Since adding 1 requires fewer operations, you would choose that option.

The solution works by examining each element x in the array and calculating x % 3 (the remainder when dividing by 3). If the remainder is 0, the element is already divisible by 3 and needs no operations. If the remainder is 1 or 2, we need to determine whether it's cheaper to subtract (reduce by the remainder amount) or add (increase by 3 - remainder) to make it divisible by 3. We take the minimum of these two options and add it to our running total.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we want to make a number divisible by 3, we need to think about what remainder it currently has when divided by 3. Any integer, when divided by 3, can only have three possible remainders: 0, 1, or 2.

If the remainder is 0, the number is already divisible by 3, so we don't need any operations.

If the remainder is 1, we have two choices:

  • Subtract 1 to make the number divisible by 3 (1 operation)
  • Add 2 to reach the next multiple of 3 (2 operations)

If the remainder is 2, we also have two choices:

  • Subtract 2 to make the number divisible by 3 (2 operations)
  • Add 1 to reach the next multiple of 3 (1 operation)

The key insight is that for any number with remainder r (where r is 1 or 2), we can either:

  • Go backward by subtracting r to reach a multiple of 3
  • Go forward by adding 3 - r to reach the next multiple of 3

Since we want to minimize operations, we always choose the smaller value between r and 3 - r. This gives us the formula min(r, 3 - r) for each element.

Because each element can be handled independently (operations on one element don't affect what we need to do with other elements), we can simply process each element separately and sum up all the minimum operations needed.

Learn more about Math patterns.

Solution Approach

The implementation follows a straightforward approach by iterating through each element in the array and calculating the minimum operations needed for that element.

For each element x in nums:

  1. Calculate the remainder when x is divided by 3: mod = x % 3

  2. If mod is 0, the element is already divisible by 3, so no operations are needed

  3. If mod is non-zero (either 1 or 2), we need to determine the minimum operations:

    • Option 1: Subtract mod from the element (requires mod operations)
    • Option 2: Add 3 - mod to the element (requires 3 - mod operations)
    • We choose the minimum: min(mod, 3 - mod)
  4. Accumulate this minimum value to our answer counter

The algorithm uses a single pass through the array with constant space complexity. The time complexity is O(n) where n is the length of the array, as we visit each element exactly once.

The Python implementation elegantly uses the walrus operator (:=) to assign and check the remainder in one line: if mod := x % 3. This allows us to both check if the remainder is non-zero and store it for use in the calculation, making the code more concise.

The total sum of all minimum operations gives us the final answer - the minimum number of operations needed to make all elements divisible by 3.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a small example array: nums = [5, 8, 10, 2]

Step 1: Process element 5

  • Calculate remainder: 5 % 3 = 2
  • Since remainder is 2, we have two options:
    • Subtract 2: 5 - 2 = 3 (2 operations)
    • Add 1: 5 + 1 = 6 (1 operation)
  • Minimum operations: min(2, 3 - 2) = min(2, 1) = 1
  • Running total: 1

Step 2: Process element 8

  • Calculate remainder: 8 % 3 = 2
  • Since remainder is 2, we have two options:
    • Subtract 2: 8 - 2 = 6 (2 operations)
    • Add 1: 8 + 1 = 9 (1 operation)
  • Minimum operations: min(2, 3 - 2) = min(2, 1) = 1
  • Running total: 1 + 1 = 2

Step 3: Process element 10

  • Calculate remainder: 10 % 3 = 1
  • Since remainder is 1, we have two options:
    • Subtract 1: 10 - 1 = 9 (1 operation)
    • Add 2: 10 + 2 = 12 (2 operations)
  • Minimum operations: min(1, 3 - 1) = min(1, 2) = 1
  • Running total: 2 + 1 = 3

Step 4: Process element 2

  • Calculate remainder: 2 % 3 = 2
  • Since remainder is 2, we have two options:
    • Subtract 2: 2 - 2 = 0 (2 operations)
    • Add 1: 2 + 1 = 3 (1 operation)
  • Minimum operations: min(2, 3 - 2) = min(2, 1) = 1
  • Running total: 3 + 1 = 4

Final Answer: 4

The minimum total number of operations needed to make all elements in [5, 8, 10, 2] divisible by 3 is 4.

Solution Implementation

1class Solution:
2    def minimumOperations(self, nums: List[int]) -> int:
3        """
4        Calculate the minimum number of operations to make all numbers divisible by 3.
5      
6        For each number, we can either add or subtract to make it divisible by 3.
7        The minimum operations needed is the smaller of:
8        - The remainder when divided by 3 (subtract this amount)
9        - 3 minus the remainder (add this amount)
10      
11        Args:
12            nums: List of integers to process
13          
14        Returns:
15            Total minimum operations needed for all numbers
16        """
17        total_operations = 0
18      
19        # Process each number in the list
20        for number in nums:
21            # Calculate remainder when divided by 3
22            remainder = number % 3
23          
24            # If remainder is 0, number is already divisible by 3, no operation needed
25            if remainder:
26                # Choose minimum between subtracting remainder or adding (3 - remainder)
27                operations_needed = min(remainder, 3 - remainder)
28                total_operations += operations_needed
29              
30        return total_operations
31
1class Solution {
2    public int minimumOperations(int[] nums) {
3        int totalOperations = 0;
4      
5        // Iterate through each number in the array
6        for (int number : nums) {
7            // Calculate the remainder when divided by 3
8            int remainder = number % 3;
9          
10            // If the number is not divisible by 3, we need operations
11            if (remainder != 0) {
12                // We can either subtract the remainder to reach a multiple of 3
13                // or add (3 - remainder) to reach the next multiple of 3
14                // Choose the minimum of these two options
15                totalOperations += Math.min(remainder, 3 - remainder);
16            }
17        }
18      
19        return totalOperations;
20    }
21}
22
1class Solution {
2public:
3    int minimumOperations(vector<int>& nums) {
4        int totalOperations = 0;
5      
6        // Iterate through each number in the array
7        for (int num : nums) {
8            // Calculate remainder when divided by 3
9            int remainder = num % 3;
10          
11            // If remainder is not 0, the number is not divisible by 3
12            if (remainder != 0) {
13                // We can either subtract remainder or add (3 - remainder) to make it divisible by 3
14                // Choose the minimum of the two options
15                totalOperations += min(remainder, 3 - remainder);
16            }
17        }
18      
19        return totalOperations;
20    }
21};
22
1/**
2 * Calculates the minimum number of operations to make all elements divisible by 3
3 * @param nums - Array of numbers to process
4 * @returns The minimum number of operations needed
5 */
6function minimumOperations(nums: number[]): number {
7    // Initialize counter for total operations
8    let totalOperations: number = 0;
9  
10    // Iterate through each number in the array
11    for (const currentNumber of nums) {
12        // Calculate remainder when divided by 3
13        const remainder: number = currentNumber % 3;
14      
15        // If remainder is not 0, the number is not divisible by 3
16        if (remainder !== 0) {
17            // Add minimum operations needed:
18            // Either subtract remainder or add (3 - remainder) to make it divisible by 3
19            totalOperations += Math.min(remainder, 3 - remainder);
20        }
21    }
22  
23    return totalOperations;
24}
25

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. This is because the algorithm iterates through each element in the array exactly once, performing a constant-time operation (modulo calculation and minimum comparison) for each element.

The space complexity is O(1). The algorithm only uses a fixed amount of extra space - the variable ans to store the result and the temporary variable mod to store the modulo result. These variables do not depend on the input size, so the space usage remains constant regardless of how large the input array is.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Overthinking the Problem with Negative Numbers

Pitfall: Developers might worry about handling negative numbers differently, thinking the modulo operation behaves unexpectedly with negatives or that the optimal strategy changes.

Why it happens: In some programming languages, modulo with negative numbers can produce negative remainders, leading to confusion about whether the same logic applies.

Solution: In Python, the modulo operation always returns a non-negative remainder between 0 and n-1 (where n is the divisor). For example, -4 % 3 = 2 and -5 % 3 = 1. The same optimization logic applies: we still choose the minimum between the remainder and 3 - remainder.

# This works correctly for both positive and negative numbers
def minimumOperations(self, nums: List[int]) -> int:
    total = 0
    for num in nums:
        remainder = num % 3  # Always 0, 1, or 2 in Python
        if remainder:
            total += min(remainder, 3 - remainder)
    return total

2. Attempting to Modify the Array In-Place

Pitfall: Some developers might think they need to actually modify the array elements to track the operations, leading to unnecessary complexity and potential bugs.

Why it happens: The problem statement mentions "add 1 to any element or subtract 1 from any element," which might suggest actual modification is needed.

Solution: The problem only asks for the total number of operations, not the final state of the array. Calculate operations without modifying the original array:

# Incorrect approach - unnecessarily modifying array
def minimumOperations_wrong(self, nums: List[int]) -> int:
    total = 0
    for i in range(len(nums)):
        while nums[i] % 3 != 0:  # Don't do this!
            if nums[i] % 3 == 1:
                nums[i] -= 1
            else:
                nums[i] += 1
            total += 1
    return total

# Correct approach - just count operations
def minimumOperations(self, nums: List[int]) -> int:
    return sum(min(x % 3, 3 - x % 3) if x % 3 else 0 for x in nums)

3. Missing the Optimization Between Add vs. Subtract

Pitfall: Implementing only one strategy (always subtract the remainder or always add to reach the next multiple) instead of choosing the minimum.

Why it happens: The initial intuition might be to always reduce to the nearest lower multiple of 3 or always increase to the next higher multiple.

Solution: Always compare both options and choose the minimum:

# Wrong - always subtracting remainder
def minimumOperations_wrong(self, nums: List[int]) -> int:
    return sum(x % 3 for x in nums)  # This would give 2 operations for remainder=2

# Correct - choosing minimum between add and subtract
def minimumOperations(self, nums: List[int]) -> int:
    total = 0
    for x in nums:
        mod = x % 3
        if mod:
            # For mod=1: min(1, 2) = 1 (subtract)
            # For mod=2: min(2, 1) = 1 (add)
            total += min(mod, 3 - mod)
    return total
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More