Subset sum. Given and a target , is there a subset summing to exactly ?
Partition. Split the multiset into two parts with equal sums (subset sum with ).

Both are NP-complete β€” but only weakly, so a pseudo-polynomial DP exists.

The DP β€”

bool subsetSum(const vector<int>& a, int S) {
    vector<bool> dp(S + 1, false);
    dp[0] = true;
    for (int x : a)
        for (int s = S; s >= x; s--)                 // DOWNWARD: each item once
            dp[s] = dp[s] || dp[s - x];
    return dp[S];
}

The downward loop is what makes each item usable once β€” see Knapsack.

With a bitset β€”

bitset<MAXS + 1> dp;
dp[0] = 1;
for (int x : a) dp |= dp << x;
bool ok = dp[S];

A 64Γ— speedup, turning into . This is the single most valuable optimisation for this problem family, and it should be the default whenever is known and the answer is boolean. See Bitset Optimization.

Huge values β€” meet in the middle

When is astronomical but :

// enumerate 2^(n/2) sums from each half; sort one, binary search the other
sort(B.begin(), B.end());
for (long long x : A) if (binary_search(B.begin(), B.end(), S - x)) return true;

time and memory. Handles -.

Schroeppel-Shamir achieves time with only memory by splitting into four parts.

The decision table

SituationMethodCost
DP
, booleanbitset DP
, huge valuesmeet in the middle
, minimise the differencecomplete Karmarkar-Karp
Approximate, large KK differencing
Few distinct valuesDP over counts, or bounded knapsack
Count the subsetsthe same DP with += instead of |
-way equal partitionbitmask DP or backtracking

The distinct-values bound

If the are distinct positive integers summing to , then β€” because . So β€œdistinct weights with a bounded total” is secretly a small- problem, and a bounded-knapsack DP over the distinct values is efficient. This observation converts several intimidating constraints into easy ones.

Minimum-difference partition

// reachable sums up to total/2; take the largest
for (int s = total / 2; s >= 0; s--)
    if (dp[s]) return total - 2 * s;

With a bitset: for (int s = total/2; s >= 0; s--) if (dp[s]) ..., or use _Find_next from the appropriate end.

Reconstruction

The boolean DP loses the which subset information. To recover it:

  • keep the full 2D table dp[i][s] and walk backwards, or
  • store, for each reachable sum, the last item that produced it (a from[s] array), which needs extra memory and works with the 1D DP.
vector<int> from(S + 1, -1);
for (int i = 0; i < n; i++)
    for (int s = S; s >= a[i]; s--)
        if (!dp[s] && dp[s - a[i]]) { dp[s] = true; from[s] = i; }
// then walk back from S
ProblemRelation
0/1 knapsacksubset sum with values
Coin changeunbounded version
Makespan / multiway partition-way generalisation
Bin packingrelated, also NP-hard
Equal-sum subsets of a given sizeadd a count dimension
Sum divisible by DP over residues, β€” polynomial
Subset XORlinear basis, β€” polynomial

The last two rows are worth noting: replacing β€œsum equals ” with β€œsum divisible by ” or β€œXOR equals ” makes the problem polynomial. Read the statement carefully β€” those variants look identical and are far easier.

See also: Knapsack Β· Meet in the Middle Β· Bitset Optimization