3012. Minimize Length of Array Using Operations
Problem Description
You have an array nums
of positive integers (0-indexed). Your goal is to minimize the array's length by repeatedly performing this operation:
- Choose two distinct indices
i
andj
where bothnums[i] > 0
andnums[j] > 0
- Calculate
nums[i] % nums[j]
and append this result to the end of the array - Remove the elements at indices
i
andj
from the array
You can perform this operation any number of times (including zero times). Return the minimum possible length of the array after performing operations.
How the operation works:
- Each operation takes two elements, removes them, and adds one new element (the modulo result)
- This reduces the array size by 1 each time
- The modulo operation
nums[i] % nums[j]
gives the remainder when dividingnums[i]
bynums[j]
Key insights from the solution:
- The minimum element
mi
in the array plays a crucial role - If any element is not divisible by
mi
, we can create an element smaller thanmi
through operations, which can then eliminate all other elements (resulting in length 1) - If all elements are divisible by
mi
, we can eliminate all larger elements usingmi
, leaving only copies ofmi
- When we have multiple copies of
mi
, we pair them up for operations. Each pair produces 0 (sincemi % mi = 0
), which gets removed in subsequent operations - With
cnt
copies ofmi
, we end up with⌈cnt/2⌉
elements remaining
The solution checks if any element has a non-zero remainder when divided by the minimum element. If yes, return 1. Otherwise, return (count of minimum element + 1) // 2
.
Intuition
The key observation is that the modulo operation a % b
always produces a result smaller than b
(when b > 0
). This means we can potentially create smaller and smaller elements through operations.
Let's think about the smallest element mi
in the array. This element is special because:
- We cannot make
mi
smaller by takingmi % anything
(sincemi % x
equalsmi
whenx > mi
) - But we can potentially create elements smaller than
mi
by takingx % mi
for other elementsx
This leads us to consider two scenarios:
Scenario 1: We can create an element smaller than mi
If there's any element x
where x % mi ≠ 0
, then 0 < x % mi < mi
. This creates an element smaller than our current minimum! Once we have this smaller element, we can use it to "consume" all other elements through repeated operations, since the modulo with the smallest element will keep producing values less than or equal to itself. Eventually, we can reduce the array to just 1 element.
Scenario 2: We cannot create anything smaller than mi
This happens when every element in the array is a multiple of mi
(i.e., x % mi = 0
for all x
). In this case:
- Operations with
mi
and larger elements produce 0 (which we can then eliminate) - Operations between two
mi
values produce 0 (mi % mi = 0
) - We can eliminate all elements larger than
mi
first, leaving only copies ofmi
- With multiple copies of
mi
, we pair them up. Each pair becomes 0 through the operation - If we have an odd number of
mi
values, one remains unpaired
Therefore, with cnt
copies of mi
, we need ⌈cnt/2⌉
operations to pair them up, leaving ⌈cnt/2⌉
elements in the end.
This intuition directly translates to the solution: check if any element has a non-zero remainder with mi
. If yes, return 1. Otherwise, count how many copies of mi
exist and return (count + 1) // 2
.
Solution Approach
The implementation follows directly from our case analysis:
Step 1: Find the minimum element
mi = min(nums)
We first identify the smallest element in the array, which is the key value that determines our strategy.
Step 2: Check if we can create a smaller element
if any(x % mi for x in nums):
return 1
This checks if there exists any element x
in nums
where x % mi ≠ 0
. The expression x % mi
evaluates to a non-zero value (truthy) when x
is not divisible by mi
. If such an element exists, we can create an element smaller than mi
through operations, which can then eliminate all other elements, leaving us with a single element.
Step 3: Handle the case where all elements are multiples of mi
return (nums.count(mi) + 1) // 2
If all elements are multiples of mi
, we know that:
- We can use
mi
to eliminate all elements larger thanmi
(sincelarger_element % mi = 0
) - We're left with only copies of
mi
- When we have
cnt
copies ofmi
, we pair them up for operations - Each pair of
mi
values produces 0 when operated on each other (mi % mi = 0
) - The formula
(cnt + 1) // 2
gives us⌈cnt/2⌉
, which is the ceiling division
The integer division (cnt + 1) // 2
effectively implements ceiling division:
- If
cnt
is even:(cnt + 1) // 2 = cnt // 2
(all elements pair up perfectly) - If
cnt
is odd:(cnt + 1) // 2 = (cnt + 1) // 2
(one element remains unpaired)
This solution is optimal with O(n)
time complexity for finding the minimum and counting occurrences, and O(1)
space complexity.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a concrete example: nums = [6, 9, 15, 3]
Step 1: Find the minimum element
mi = min([6, 9, 15, 3]) = 3
Step 2: Check if we can create a smaller element
- Check each element's remainder when divided by 3:
6 % 3 = 0
(6 is divisible by 3)9 % 3 = 0
(9 is divisible by 3)15 % 3 = 0
(15 is divisible by 3)3 % 3 = 0
(3 is divisible by itself)
- Since all remainders are 0, we cannot create an element smaller than 3
Step 3: Count occurrences of minimum and calculate result
- Count of
mi
(which is 3) in the array: 1 - Result:
(1 + 1) // 2 = 2 // 2 = 1
So the minimum possible length is 1.
Another example to illustrate the other case: nums = [8, 5, 10]
Step 1: Find the minimum element
mi = min([8, 5, 10]) = 5
Step 2: Check if we can create a smaller element
- Check each element's remainder when divided by 5:
8 % 5 = 3
(not divisible by 5!)5 % 5 = 0
(5 is divisible by itself)10 % 5 = 0
(10 is divisible by 5)
- Since
8 % 5 = 3
, which is non-zero, we can create an element (3) smaller than our minimum (5)
Step 3: Return 1
- Because we found a non-zero remainder, we can reduce the array to a single element
- Result: 1
The key insight: once we have 3 (which is smaller than 5), we can use it to eventually eliminate all other elements through repeated operations, since any number modulo 3 will produce a value ≤ 3.
Solution Implementation
1class Solution:
2 def minimumArrayLength(self, nums: List[int]) -> int:
3 # Find the minimum value in the array
4 min_value = min(nums)
5
6 # Check if any element in the array is NOT divisible by the minimum value
7 # If such an element exists, we can always reduce the array to length 1
8 if any(num % min_value != 0 for num in nums):
9 return 1
10
11 # If all elements are divisible by the minimum value,
12 # count how many times the minimum value appears
13 # The minimum array length is ceiling of (count of minimum / 2)
14 # which is equivalent to (count + 1) // 2
15 min_count = nums.count(min_value)
16 return (min_count + 1) // 2
17
1class Solution {
2 public int minimumArrayLength(int[] nums) {
3 // Find the minimum value in the array
4 int minValue = Arrays.stream(nums).min().getAsInt();
5
6 // Count occurrences of the minimum value
7 int minCount = 0;
8
9 // Iterate through all elements in the array
10 for (int num : nums) {
11 // If any element is not divisible by the minimum value,
12 // we can reduce the array to length 1
13 if (num % minValue != 0) {
14 return 1;
15 }
16
17 // Count how many times the minimum value appears
18 if (num == minValue) {
19 minCount++;
20 }
21 }
22
23 // The minimum array length is ceiling of (count of min values / 2)
24 // This is because we can pair up minimum values to eliminate them
25 return (minCount + 1) / 2;
26 }
27}
28
1class Solution {
2public:
3 int minimumArrayLength(vector<int>& nums) {
4 // Find the minimum element in the array
5 int minValue = *min_element(nums.begin(), nums.end());
6
7 // Count occurrences of the minimum value
8 int minCount = 0;
9
10 // Iterate through all elements in the array
11 for (int num : nums) {
12 // If any element is not divisible by the minimum value,
13 // we can reduce the array to length 1
14 if (num % minValue != 0) {
15 return 1;
16 }
17
18 // Count how many times the minimum value appears
19 if (num == minValue) {
20 minCount++;
21 }
22 }
23
24 // If all elements are divisible by the minimum value,
25 // the minimum possible array length is ceil(minCount / 2)
26 // which is equivalent to (minCount + 1) / 2
27 return (minCount + 1) / 2;
28 }
29};
30
1/**
2 * Finds the minimum possible length of the array after performing operations.
3 * The operation involves selecting two elements and replacing them with their remainder.
4 *
5 * @param nums - The input array of positive integers
6 * @returns The minimum possible array length after operations
7 */
8function minimumArrayLength(nums: number[]): number {
9 // Find the minimum value in the array
10 const minValue = Math.min(...nums);
11
12 // Count occurrences of the minimum value
13 let minValueCount = 0;
14
15 // Iterate through each element in the array
16 for (const currentNumber of nums) {
17 // If any number is not divisible by the minimum value,
18 // we can reduce the array to length 1
19 if (currentNumber % minValue !== 0) {
20 return 1;
21 }
22
23 // Count how many times the minimum value appears
24 if (currentNumber === minValue) {
25 minValueCount++;
26 }
27 }
28
29 // Return ceiling of (minValueCount / 2)
30 // Using bit shift: (count + 1) >> 1 is equivalent to Math.ceil(count / 2)
31 return (minValueCount + 1) >> 1;
32}
33
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because:
- Finding the minimum element using
min(nums)
requiresO(n)
time to traverse all elements - The generator expression
any(x % mi for x in nums)
iterates through the array once in the worst case, takingO(n)
time - Counting occurrences with
nums.count(mi)
requires anotherO(n)
traversal - These operations are performed sequentially, resulting in
O(n) + O(n) + O(n) = O(n)
overall
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space:
- The variable
mi
stores a single integer - The generator in
any()
usesO(1)
space as it processes elements one at a time without storing them - The count operation returns a single integer
- No additional data structures are created that scale with input size
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Operation's Effect on Array Size
A common mistake is thinking that the operation always reduces the array size by 1. While this is true for non-zero results, when nums[i] % nums[j] = 0
, the zero gets added to the array but can be removed in the next operation (since we need both elements to be positive). This confusion can lead to incorrect calculations of the final array length.
Solution: Remember that zeros resulting from operations effectively disappear since they cannot be used in subsequent operations (which require positive integers).
2. Incorrectly Handling the Case Where All Elements Equal the Minimum
Some might think that if all elements are equal to the minimum value, the answer should always be 1. However, when you have multiple copies of the same value x
, performing x % x = 0
creates zeros that get eliminated, but you need pairs to do this operation.
Example: If nums = [3, 3, 3]
, you can't reduce it to length 1. You can only pair two 3's to get 0, leaving you with one 3, so the answer is 2, not 1.
Solution: Use the ceiling division formula (count + 1) // 2
to correctly handle both even and odd counts of the minimum value.
3. Not Recognizing the Special Role of the Minimum Element
A pitfall is attempting complex strategies without recognizing that the minimum element is the key. Some might try to find GCD of all elements or use other mathematical properties, missing the simpler insight that any element not divisible by the minimum can create an even smaller element.
Solution: Always start by finding the minimum element and check divisibility against it first. This immediately tells you whether you can achieve length 1.
4. Integer Division vs. Ceiling Division Confusion
Using regular integer division count // 2
instead of ceiling division (count + 1) // 2
will give incorrect results when the count is odd.
Example: With 5 copies of the minimum value, 5 // 2 = 2
but the correct answer is 3 (you can pair 4 of them, leaving 3 elements).
Solution: Always use (count + 1) // 2
for ceiling division in Python when working with positive integers.
What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!