Split the input in half, enumerate each half separately, and combine. Turns into — the difference between and .

The pattern

1. Split the n items into halves A and B.
2. Enumerate all 2^(n/2) subsets of A, storing their values.
3. Enumerate all 2^(n/2) subsets of B; for each, find the matching partner in A.

The combine step is a sort + binary search, a hash map, or a two-pointer scan.

Worked example: subset sum

“Is there a subset summing to exactly ?” with .

bool subsetSum(vector<long long>& a, long long S) {
    int n = a.size(), h = n / 2;
    vector<long long> A, B;
    for (int m = 0; m < (1 << h); m++) {
        long long s = 0;
        for (int i = 0; i < h; i++) if (m >> i & 1) s += a[i];
        A.push_back(s);
    }
    for (int m = 0; m < (1 << (n - h)); m++) {
        long long s = 0;
        for (int i = 0; i < n - h; i++) if (m >> i & 1) s += a[h + i];
        B.push_back(s);
    }
    sort(B.begin(), B.end());
    for (long long x : A)
        if (binary_search(B.begin(), B.end(), S - x)) return true;
    return false;
}

time and memory. For that is per half — instant.

The variants of the combine step

GoalCombine
Exact sum hash set, or sort + binary search
Closest to sort , lower_bound and check both neighbours
Sum , maximisesort , keep a prefix maximum, binary search
Count the wayssort , equal_range
Sum in a rangetwo lower_bound calls
Maximise a value under a weight limitsort by weight, prefix-max the value, binary search
Both halves constrainedsort both, two pointers

The prefix-maximum trick

For “maximise the value with total weight ”:

sort(B.begin(), B.end());                              // by weight
for (int i = 1; i < (int)B.size(); i++)
    B[i].value = max(B[i].value, B[i-1].value);        // prefix max
// then for each a in A: binary search the largest weight <= W - a.weight

Without the prefix maximum you would need the maximum over a range; with it, the binary search alone suffices.

Where it applies

ProblemNote
Subset sum / partition40-45the canonical case
Knapsack with huge weights40when the DP’s is too large
Count subsets with a given XOR40hash map on the XOR
4-SUMpair sums from each half,
Shortest path with edgesbidirectional BFS
Discrete log (baby-step giant-step) from each side
Solve , the same splitting
Puzzle solving (15-puzzle, Rubik’s)bidirectional BFS
Count paths of length in a graphmeet at the midpoint
Equation enumerate each side

Bidirectional search — the graph version

Search forward from the start and backward from the goal, stopping when the frontiers meet:

// BFS from both ends, always expanding the smaller frontier
while (!fwd.empty() && !bwd.empty()) {
    if (fwd.size() <= bwd.size()) expand(fwd, visitedF, visitedB, ans);
    else                          expand(bwd, visitedB, visitedF, ans);
}

Expanding the smaller frontier keeps the balance. This is what makes the 15-puzzle and similar state-space searches tractable.

Memory — the real constraint

entries at 8 bytes:

Entries per halfMemory
300.3 MB
408 MB
4434 MB
50268 MB — too much

Mitigations: store only one half and stream the other; use an unequal split (a smaller stored half); or apply Schroeppel-Shamir, which solves subset sum in time but only memory by splitting into four parts and merging with priority queues.

Choosing the split

An even split minimises . But if one side’s enumeration is cheaper or its combine step is heavier, shifting the split can help. When memory is the binding constraint, store the smaller half.

See also: Subset Sum · Branch and Bound · Discrete Logarithm