9.9

From The Algorithm Design Manual Solution Wiki
Jump to navigation Jump to search

Back-tracking scheme
1. Sort the multiset once (so equal numbers are consecutive).
2. Process the numbers left-to-right; for the i-th distinct value v occurring c times you may pick it 0…c times, provided the running sum never exceeds t.
3. Whenever the remaining total becomes 0 the current multiset of picks is a valid solution; store a copy.
4. Because each element is handled only from its first appearance onward, every combination is produced in non-decreasing order ⇒ no duplicates.

def subset_sum(multiset, t): multiset.sort() # compress into (value, multiplicity) nums = [] for v in multiset: if not nums or nums[-1][0] != v: nums.append([v, 1]) else: nums[-1][1] += 1 out, cur = [], [] def dfs(idx, remaining): if remaining == 0: out.append(cur.copy()) return if idx == len(nums) or remaining < 0: return val, cnt = nums[idx] # try picking the value 0..cnt times for k in range(cnt + 1): if k * val > remaining: break cur.extend([val] * k) dfs(idx + 1, remaining - k * val) del cur[-k:] # back-track dfs(0, t) return out

Example
t = 4, S = [4,3,2,2,1,1] produces:
[[4], [3,1], [2,2], [2,1,1]]

Complexity
Let M be the number of valid subsets. Time: O(M + 2n) in the worst case (every subset explored), but far smaller in practice because branches that overshoot t are pruned early. Space: O(n) for the recursion stack plus O(M · n) to store the results.


Back to Chapter 9