Taro Logo

Minimum Operations to Form Subsequence With Target Sum

Hard
Amazon logo
Amazon
2 views
Topics:
Greedy AlgorithmsBit Manipulation

You are given a 0-indexed array nums consisting of non-negative powers of 2, and an integer target.

In one operation, you must apply the following changes to the array:

  • Choose any element of the array nums[i] such that nums[i] > 1.
  • Remove nums[i] from the array.
  • Add two occurrences of nums[i] / 2 to the end of nums.

Return the minimum number of operations you need to perform so that nums contains a subsequence whose elements sum to target. If it is impossible to obtain such a subsequence, return -1.

A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements.

Example 1:

Input: nums = [1,2,8], target = 7
Output: 1
Explanation: In the first operation, we choose element nums[2]. The array becomes equal to nums = [1,2,4,4].
At this stage, nums contains the subsequence [1,2,4] which sums up to 7.
It can be shown that there is no shorter sequence of operations that results in a subsequnce that sums up to 7.

Example 2:

Input: nums = [1,32,1,2], target = 12
Output: 2
Explanation: In the first operation, we choose element nums[1]. The array becomes equal to nums = [1,1,2,16,16].
In the second operation, we choose element nums[3]. The array becomes equal to nums = [1,1,2,16,8,8]
At this stage, nums contains the subsequence [1,1,2,8] which sums up to 12.
It can be shown that there is no shorter sequence of operations that results in a subsequence that sums up to 12.

Example 3:

Input: nums = [1,32,1], target = 35
Output: -1
Explanation: It can be shown that no sequence of operations results in a subsequence that sums up to 35.

Constraints:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 2^30
  • nums consists only of non-negative powers of two.
  • 1 <= target < 2^31

Solution


Naive Approach

A brute force approach would be to generate all possible subsequences of nums after each operation and check if any subsequence sums up to the target. This approach is highly inefficient due to the exponential number of subsequences and repeated calculations.

Optimal Approach

We can solve this problem using a greedy approach combined with bit manipulation. The idea is to iterate from the most significant bit to the least significant bit of the target. For each bit, we try to find a number in nums that has that bit set. If we find such a number, we subtract that number from the target. If we don't find such a number, we try to generate it by performing operations on larger numbers in nums. The number of operations needed to generate the required number will be the answer.

  1. Count occurrences: Count the occurrences of each power of 2 in nums.
  2. Iterate through bits: Iterate from the most significant bit (MSB) of target down to the least significant bit (LSB).
  3. Check if bit is set: If the current bit is set in target, check if we have that power of 2 in nums.
    • If yes, subtract that power of 2 from target.
    • If no, try to generate that power of 2 by splitting larger powers of 2.
  4. Splitting: If we don't have the required power of 2, look for the next largest power of 2 that exists in nums. Split this number repeatedly until we get the required power of 2.
  5. Impossible case: If, at any point, the target is still greater than 0, but we cannot generate the required power of 2, return -1.

Edge Cases

  • If the sum of all elements in nums is less than target, it is impossible to form the target.
  • If target is 0, the answer is 0 because an empty subsequence has a sum of 0.
  • If we run out of numbers to split and the target isn't met, return -1.

Code

def minOperations(nums, target):
    counts = {}
    for num in nums:
        counts[num] = counts.get(num, 0) + 1

    operations = 0
    for bit in range(30, -1, -1):
        power_of_2 = 1 << bit
        if target >= power_of_2 and (target & power_of_2):
            if power_of_2 in counts and counts[power_of_2] > 0:
                target -= power_of_2
                counts[power_of_2] -= 1
            else:
                found = False
                for higher_bit in range(bit + 1, 31):
                    higher_power_of_2 = 1 << higher_bit
                    if higher_power_of_2 in counts and counts[higher_power_of_2] > 0:
                        counts[higher_power_of_2] -= 1
                        diff = higher_bit - bit
                        operations += diff
                        counts[power_of_2] = counts.get(power_of_2, 0) + 1
                        
                        remaining = higher_power_of_2 // 2
                        while remaining != power_of_2:
                            counts[remaining] = counts.get(remaining, 0) + 2
                            remaining //= 2
                        
                        counts[power_of_2] -= 1
                        target -= power_of_2
                        found = True
                        break
                if not found:
                    return -1

    if target == 0:
        return operations
    else:
        return -1

Big O Analysis

  • Time Complexity: O(N + 30*30) = O(N), where N is the length of nums. The outer loop iterates at most 30 times (number of bits in a 2^30). The inner loop iterates at most 30 times. The operations inside take constant time. The first loop to count the occurences of elements in nums is O(N).
  • Space Complexity: O(K), where K is the number of unique elements in nums. In the worst case, K can be equal to N, making it O(N). This is due to the counts dictionary.