AlgoMaster Logo

Combination Sum

mediumFrequency6 min readUpdated June 23, 2026

Understanding the Problem

We're given an array of distinct positive integers and a target sum. We need to find every way to pick numbers from the array (with unlimited reuse) that add up exactly to the target. The order of numbers in a combination doesn't matter, so [2, 2, 3] and [2, 3, 2] are the same combination and should only appear once.

Unlike problems where each element can be used at most once, the same candidate can appear in a combination any number of times. That makes the search space large, but the constraints (target at most 40, every candidate at least 2) keep it small enough to enumerate.

This is a backtracking problem. We explore all ways to build up to the target by trying each candidate and recursing with the remaining sum. To avoid duplicate combinations, we enforce an ordering: once we move past a candidate, we never go back to it.

Key Constraints:

  • 1 <= candidates.length <= 30 → Up to 30 candidates, small enough for backtracking with pruning.
  • 2 <= candidates[i] <= 40 → Every candidate is at least 2, so the recursion depth is at most target / min(candidates) = 40 / 2 = 20.
  • 1 <= target <= 40 → A small target keeps the total number of valid combinations manageable (the problem guarantees fewer than 150 combinations).
  • All elements are distinct → Duplicate candidates cannot produce duplicate combinations; the only duplication to prevent comes from reordering the same picks.

Approach 1: Brute Force (Generate All Combinations)

Intuition

For each candidate, decide how many times to include it (0, 1, 2, ... up to target / candidate), then move on to the next candidate. After a decision has been made for every candidate, check if the total sum equals the target.

This enumerates every possible "frequency vector." With candidates [2, 3, 7] and target 7, we try (0 twos, 0 threes, 0 sevens), (1 two, 0 threes, 0 sevens), ..., (3 twos, 0 threes, 0 sevens), (0 twos, 1 three, 0 sevens), and so on. Most of these overshoot the target or fall short, but we check every possibility.

This approach prunes nothing beyond capping each count so the running sum stays within the target. It generates all frequency assignments and only filters valid ones at the end. Duplicates are impossible by construction: a combination is determined by how many copies of each candidate it contains, and each frequency vector is visited once.

Algorithm

  1. Start with the first candidate and an empty combination.
  2. For the current candidate, try using it 0 times, 1 time, 2 times, etc., up to the point where the sum would exceed the target.
  3. For each choice, move on to the next candidate and repeat.
  4. After processing all candidates, if the running sum equals the target, record the combination.
  5. Collect all valid combinations and return them.

Example Walkthrough

The trace below uses candidates = [2, 3, 6, 7] and target = 7. Counts are tried in ascending order, so the branch with zero copies of everything except one 7 is explored first, and [7] is found before [2, 2, 3].

candidates
1index=0, remaining=7: try 0, 1, 2, or 3 copies of 2, starting with 0
0
2
x0
1
3
2
6
3
7
current
1Start: empty combination, remaining = 7
1/8

Code

This approach commits to a full count for each candidate before it can tell whether the branch is feasible. The next approach makes one decision at a time and abandons a branch as soon as the remaining target can no longer be reached.

Approach 2: Backtracking with Pruning (Optimal)

Intuition

Instead of deciding how many times to use each candidate upfront, backtracking makes one decision at a time: pick one candidate, subtract it from the target, and recurse with the reduced target. If the remaining target reaches zero, we found a valid combination. If a candidate exceeds the remaining target, we stop extending that branch and backtrack.

Duplicates are avoided with a start index. When we pick the candidate at index i, the recursive call considers only candidates at index i or later. We never pick candidate[2] after candidate[3], so [3, 2] is never generated as a separate combination from [2, 3].

Sorting the candidates first enables one more pruning step. If the current candidate exceeds the remaining target, every later candidate does too (the array is in ascending order), so the loop can break immediately instead of checking each one.

Algorithm

  1. Sort the candidates array in ascending order.
  2. Start a recursive backtracking function with the full target and start index 0.
  3. If the remaining target is 0, we found a valid combination. Add it to the result.
  4. Iterate through candidates starting from the current start index.
  5. If the current candidate exceeds the remaining target, break (all further candidates are larger).
  6. Add the candidate to the current combination.
  7. Recurse with the reduced target and the same start index (since we can reuse the candidate).
  8. Remove the last candidate (backtrack) and try the next one.

Example Walkthrough

The same input, traced with the pruned backtracking. Each recursive call resumes its loop at the index of the last pick, and a branch ends as soon as the next candidate exceeds the remaining target.

candidates
1Sorted candidates. Start: remaining=7, pick candidate[0]=2
0
2
pick
1
3
2
6
3
7
current
1Start empty, remaining = 7
1/9

Code