Knowledge Guide
HomeDSAAdvanced Patterns

hard Closest Subsequence Sum

Problem Statement

Given an integer array nums and an integer goal, find a subsequence of nums such that the sum of its elements is as close as possible to goal.

In other words, if the sum of the subsequence's elements is sum, then you need to minimize the absolute difference abs(sum - goal).

Return the minimum possible value of abs(sum - goal).

A subsequence is formed by removing some elements (possibly all or none) from the original array.

Examples

Example 1:

Example 2:

Example 3:

Constraints:

Try it yourself

Try solving this question here:

✅ Solution Closest Subsequence Sum

Problem Statement

Given an integer array nums and an integer goal, find a subsequence of nums such that the sum of its elements is as close as possible to goal.

In other words, if the sum of the subsequence's elements is sum, then you need to minimize the absolute difference abs(sum - goal).

Return the minimum possible value of abs(sum - goal).

A subsequence is formed by removing some elements (possibly all or none) from the original array.

Examples

Example 1:

  • Input: nums = [1, 2, 3, 1, 1], goal = 6
  • Expected Output: 0
  • Explanation: The subsequence [1, 2, 3], [2, 3, 1] or [1, 3, 1, 1] has a sum of 6, which matches the goal. The absolute difference is 0.

Example 2:

  • Input: nums = [1, 2, 9, -6, 15], goal = 7
  • Expected Output: 1
  • Explanation: The subsequence [1, 2, 9, -6] has a sum of 6, which is closest to goal. The absolute difference is 1.

Example 3:

  • Input: nums = [1, 2, 4, 5], goal = -1
  • Expected Output: 1
  • Explanation: The subsequence [] (empty subsequence) has a sum of 0, which exactly matches the goal. The absolute difference is 1.

Constraints:

  • 1 <= nums.length <= 40
  • -107 <= nums[i] <= 107
  • -109 <= goal <= 109

Solution

To solve this problem, we use the "Meet in the Middle" approach. This strategy is effective because it splits the problem into two smaller subproblems, which makes it easier to manage and compute all possible subsequence sums.

The first step involves generating all possible subsequence sums for both halves of the array. We then sort the sums from one half to enable fast lookup using binary search. For each sum from the first half, we calculate the difference between the goal and that sum, then find the closest matching sum from the sorted list of sums from the other half. By minimizing this difference, we find the sum that is closest to the goal. This approach significantly reduces the problem's complexity by leveraging binary search on a sorted array of subsequence sums.

Step-by-Step Algorithm

  1. Divide the Array:

    • Split the array nums into two halves.
    • Store the first half in an array called left and the second half in an array called right.
  2. Generate Subsequences Sums for Each Half:

    • For each half (left and right), generate all possible sums of their subsequences.
    • To do this, iterate through all possible binary representations of numbers up to 2^(size of half). For each binary number, add the corresponding elements in the half to the sum if the corresponding bit is set.
    • Store the sums for the left half in a list called leftSums and for the right half in a list called rightSums.
  3. Sort the Sums from the Right Half:

    • Sort the list rightSums to enable efficient binary search later.
  4. Initialize Minimum Difference:

    • Initialize a variable minDiff to store the minimum absolute difference found. Set it to a large value (e.g., Integer.MAX_VALUE).
  5. Find the Closest Sum to the Goal:

    • For each sum in leftSums, calculate the target difference target by subtracting the sum from the goal.
    • First, update minDiff by comparing the current minDiff with the absolute value of target (this accounts for the case where the subsequence is from the left half only).
    • Use binary search on rightSums to find the closest possible sum that would make the sum of both halves as close as possible to the goal.
  6. Binary Search and Handle Non-Exact Matches:

    • If the binary search does not find an exact match, the Collections.binarySearch() method returns a negative index. This negative index can be used to identify where the target would be inserted in the sorted list to maintain order.
    • Convert the negative index idx to a positive index by calculating idx = -idx - 1. This index now points to the first element in rightSums that is greater than the target.
    • Check the Closest Larger Value:
      • If idx is less than the size of rightSums, compare the absolute difference between the target and the sum at this index (rightSums.get(idx)). Update minDiff if this difference is smaller than the current minDiff.
    • Check the Closest Smaller Value:
      • If idx is greater than 0, it means there is a value in rightSums that is smaller than the target. Compare the absolute difference between the target and the sum at rightSums.get(idx - 1). Again, update minDiff if this difference is smaller.
  7. Return the Minimum Difference:

    • After evaluating all possible subsequence sums, return minDiff as the closest difference between any subsequence sum and the goal.

Algorithm Walkthrough

Let's walk through the expanded explanation for the key step using the example nums = [1, 2, 3, 1, 1] and goal = 6.

  1. Divide the Array:

    • Split nums into:
      • left = [1, 2]
      • right = [3, 1, 1]
  2. Generate Subsequences Sums:

    • For left:
      • Possible subsequences: [], [1], [2], [1, 2]
      • Corresponding sums: 0, 1, 2, 3
      • leftSums = [0, 1, 2, 3]
    • For right:
      • Possible subsequences: [], [3], [1], [1], [3, 1], [3, 1], [1, 1], [3, 1, 1]
      • Corresponding sums: 0, 3, 1, 1, 4, 4, 2, 5
      • rightSums = [0, 1, 1, 2, 3, 4, 4, 5] (after sorting)
  3. Initialize Minimum Difference:

    • Set minDiff = Integer.MAX_VALUE
  4. Find the Closest Sum to the Goal:

    • For sum = 0 in leftSums, calculate target = 6 - 0 = 6.
  5. Binary Search and Handle Non-Exact Matches:

    • Perform binary search on rightSums for target = 6.
    • The search does not find an exact match, returning idx = -9 (indicating where 6 would be inserted to keep the list sorted).
    • Convert idx to a positive index: idx = -idx - 1 = 8.
    • Check the Closest Larger Value:
      • idx = 8 is equal to the size of rightSums, so we skip this check (no element larger than target).
    • Check the Closest Smaller Value:
      • Since idx > 0, check rightSums.get(7), which is 5.
      • Calculate the difference: abs(6 - 5) = 1.
      • Update minDiff to 1.
  6. Continue Checking Other Sums:

    • For sum = 1 in leftSums, calculate target = 6 - 1 = 5.
    • Binary search on rightSums finds an exact match 5 at index 7, so return 0 immediately.

Code

java
import java.util.*;

public class Solution {

  // Method to find the closest subsequence sum to the goal
  public int minAbsDifference(int[] nums, int goal) {
    // Divide the array into two halves
    int n = nums.length;
    int[] left = Arrays.copyOfRange(nums, 0, n / 2);
    int[] right = Arrays.copyOfRange(nums, n / 2, n);

    // Generate all possible sums for both halves
    List<Integer> leftSums = generateSubsequenceSums(left);
    List<Integer> rightSums = generateSubsequenceSums(right);

    // Sort the right half sums for binary search
    Collections.sort(rightSums);

    int minDiff = Integer.MAX_VALUE;

    // Compare each sum from the left half with the closest sum from the right half
    for (int sum : leftSums) {
      int target = goal - sum;
      minDiff = Math.min(minDiff, Math.abs(target)); // Compare with 0 (empty subsequence)

      int idx = Collections.binarySearch(rightSums, target);
      if (idx >= 0) {
        return 0; // If exact match is found
      }

      // If no exact match, find the closest values
      idx = -idx - 1;
      if (idx < rightSums.size()) {
        minDiff = Math.min(minDiff, Math.abs(target - rightSums.get(idx)));
      }
      if (idx > 0) {
        minDiff = Math.min(minDiff, Math.abs(target - rightSums.get(idx - 1)));
      }
    }

    return minDiff;
  }

  // Method to generate all possible sums of subsequences for a given array
  private List<Integer> generateSubsequenceSums(int[] nums) {
    List<Integer> result = new ArrayList<>();
    int n = nums.length;
    for (int i = 0; i < (1 << n); i++) {
      int sum = 0;
      for (int j = 0; j < n; j++) {
        if ((i & (1 << j)) != 0) {
          sum += nums[j];
        }
      }
      result.add(sum);
    }
    return result;
  }

  public static void main(String[] args) {
    Solution solution = new Solution();

    // Test case 1
    int[] nums1 = { 1, 2, 3, 1, 1 };
    int goal1 = 6;
    System.out.println(
      "Actual Output: " + solution.minAbsDifference(nums1, goal1)
    );

    // Test case 2
    int[] nums2 = { 1, 2, 9, -6, 15 };
    int goal2 = 7;
    System.out.println(
      "Actual Output: " + solution.minAbsDifference(nums2, goal2)
    );

    // Test case 3
    int[] nums3 = { 1, 2, 4, 5 };
    int goal3 = -1;
    System.out.println(
      "Actual Output: " + solution.minAbsDifference(nums3, goal3)
    );
  }
}

Complexity Analysis

Time Complexity

  1. Generating Subsequences Sums:

    • For each half of the array, we generate all possible sums of subsequences. Since there are 2(n/2) subsequences for an array of size n/2, the time complexity for generating these sums is O(2(n/2) * (n/2)).
  2. Sorting the Right Half Sums:

    • Sorting the sums of the right half takes O(2(n/2) * log(2(n/2)).
  3. Binary Search:

    • For each sum in the left half (2(n/2) sums), we perform a binary search on the sorted list of sums from the right half. The time complexity for this step is O(2(n/2)) * log(2(n/2))).

Combining all these steps, the overall time complexity of the algorithm is: O(2(n/2) * (n/2) + 2(n/2) * log(2(n/2))) This simplifies to: O(n * 2(n/2))

Space Complexity

  • The space complexity is mainly due to storing the subsequence sums for both halves of the array, which requires O(2(n/2)) space for each half. Hence, the overall space complexity is: O(2(n/2))
🤖 Don't fully get this? Learn it with Claude

Stuck on Closest Subsequence Sum? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **Closest Subsequence Sum** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **Closest Subsequence Sum** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **Closest Subsequence Sum**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **Closest Subsequence Sum**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes