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
| Situation | Method | Cost |
|---|---|---|
| DP | ||
| , boolean | bitset DP | |
| , huge values | meet in the middle | |
| , minimise the difference | complete Karmarkar-Karp | |
| Approximate, large | KK differencing | |
| Few distinct values | DP over counts, or bounded knapsack | |
| Count the subsets | the same DP with += instead of | | |
| -way equal partition | bitmask 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 SRelated problems
| Problem | Relation |
|---|---|
| 0/1 knapsack | subset sum with values |
| Coin change | unbounded version |
| Makespan / multiway partition | -way generalisation |
| Bin packing | related, also NP-hard |
| Equal-sum subsets of a given size | add a count dimension |
| Sum divisible by | DP over residues, β polynomial |
| Subset XOR | linear 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