You are given a 0-indexed array nums consisting of positive integers.
There are two types of operations that you can apply on the array any number of times:
Return the minimum number of operations required to make the array empty, or -1 if it is not possible.
Example 1:
Input: nums = [2,3,3,2,2,4,2,3,4]
Output: 4Explanation: We can apply the following operations to make the array empty:
Example 2:
Input: nums = [2,1,2,2,3,3]
Output: -1Explanation: It is impossible to empty the array.
Constraints:
2 <= nums.length <= 100,0001 <= nums[i] <= 1,000,000Before attempting this problem, you should be comfortable with:
We can only delete elements in groups of 2 or 3, and all elements in a group must be identical. This means we need to count the frequency of each element and figure out how to reduce each frequency to zero using the minimum number of deletions.
For any count, we try both options: subtract 2 or subtract 3, and recursively solve for the remaining count. If we reach exactly 0, we are done. If we go negative, that path is invalid. The min of both branches gives us the answer.
count, use recursion to find the minimum operations:count is 0, return 0. If count is negative, return infinity.dfs(count - 2) and dfs(count - 3), take the min, and add 1.count returns infinity, return -1 (impossible to empty).class Solution:
def minOperations(self, nums: List[int]) -> int:
def dfs(cur):
if cur < 0:
return float('inf')
if cur == 0:
return 0
ops = min(dfs(cur - 2), dfs(cur - 3))
return 1 + ops
count = Counter(nums)
res = 0
for num, cnt in count.items():
op = dfs(cnt)
if op == float("inf"):
return -1
res += op
return resWhere is the size of the array and is the average frequency of the array elements.
The recursive solution has overlapping subproblems. When computing the minimum operations for a count, we may revisit the same count multiple times. By caching results, we avoid redundant computation and make the solution efficient.
cache) to cache results for each count value.count, if it equals 2 or 3, return 1 (one operation suffices).min(dfs(count - 2), dfs(count - 3)) + 1 and cache it.-1 if any frequency is impossible.class Solution:
def minOperations(self, nums: List[int]) -> int:
cache = {}
def dfs(num):
if num < 0:
return float("inf")
if num in [2, 3]:
return 1
if num in cache:
return cache[num]
res = min(dfs(num - 2), dfs(num - 3))
cache[num] = res + 1
return cache[num]
count = Counter(nums)
res = 0
for num, cnt in count.items():
op = dfs(cnt)
if op == float("inf"):
return -1
res += op
return resWhere is the size of the array and is the average frequency of the array elements.
Instead of solving recursively from larger counts down to zero, we can build up solutions from smaller counts. We precompute the minimum operations for all counts from 0 up to the maximum frequency, using the recurrence relation derived earlier.
maxf (maximum frequency).minOps where minOps[i] stores the minimum operations to reduce count i to zero.minOps[0] = 0 and minOps[1] = infinity (count of 1 is impossible).count from 2 to maxf, compute minOps[i] = min(minOps[i-2], minOps[i-3]) + 1.minOps and sum the results. Return -1 if any is infinity.class Solution:
def minOperations(self, nums: List[int]) -> int:
count = Counter(nums)
maxf = max(count.values())
minOps = [0] * (maxf + 1)
minOps[1] = float("inf")
for i in range(2, maxf + 1):
minOps[i] = minOps[i - 2]
if i - 3 >= 0:
minOps[i] = min(minOps[i], minOps[i - 3])
if minOps[i] != float("inf"):
minOps[i] += 1
res = 0
for num, cnt in count.items():
op = minOps[cnt]
if op == float("inf"):
return -1
res += op
return resWhere is the size of the array and is the average frequency of the array elements.
There is a mathematical pattern: for any count greater than 1, the minimum operations is the ceiling of count / 3. This works because we prioritize groups of 3, and any remainder can be handled by converting some 3s to 2s. For example, count = 4 uses two 2s, count = 5 uses one 2 and one 3.
The only impossible case is when count equals 1, since we need at least 2 identical elements to perform any deletion.
1, return -1 (impossible).ceil(count / 3) to the res.When an element appears exactly once, it is impossible to delete it since the minimum group size is 2. Failing to check for frequencies of 1 and return -1 will produce incorrect results for inputs that cannot be emptied.
The greedy solution requires ceiling division (ceil(count / 3)), not floor division. Using count / 3 will undercount operations for counts like 4, 5, 7, or 8 where remainders require additional groups.
In some languages, when using the formula (count + 2) / 3 for ceiling division, adding 2 to a very large count could cause overflow. While unlikely given typical constraints, be aware of this when working with custom large inputs.