// Given a list of lists of possible cage values: // [[1,2,3], [3,4,5]] // Recursively generates tuples of combinations from each of the lists as // follows: // [1,3] // [1,4] // [1,5] // [2,3] // [2,4] // ... etc // Each of these is checked against the target sum, and pushed into a result // vector if they match. // Note: The algorithm assumes that the list of possibles/candidates are // ordered. This allows it to bail out early if it detects there's no point // going further. static void subsetSum(const std::vector<IntList> &possible_lists, const std::size_t p_size, IntList &tuple, unsigned tuple_sum, std::vector<IntList> &subsets, const unsigned target_sum, unsigned list_idx) { for (unsigned p = list_idx; p < p_size; ++p) { for (auto &poss : possible_lists[p]) { // Optimization for small target sums: if the candidate is bigger than // the target itself then it can't be valid, neither can any candidate // after it (ordered). if (target_sum < static_cast<unsigned>(poss)) { break; } // Can't repeat a value inside a cage if (std::find(tuple.begin(), tuple.end(), poss) != tuple.end()) { continue; } // Pre-calculate the new tuple values to avoid spurious // insertions/deletions to the vector. const auto new_tuple_sum = tuple_sum + poss; const auto new_tuple_size = tuple.size() + 1; // If we've added too much then we can bail out (ordered). if (new_tuple_sum > target_sum) { break; } // If there are fewer spots left in the tuple than there are options for // the sum to reach the target, bail. // TODO: This could be more sophisticated (can't have more than one 1, so // it's more like the N-1 sum that it should be greater than. if ((p_size - new_tuple_size) > (target_sum - new_tuple_sum)) { break; } if (new_tuple_size == p_size) { // If we've reached our target size then we can stop searching other // possiblities from this list (ordered). if (new_tuple_sum == target_sum) { tuple.push_back(poss); subsets.push_back(tuple); tuple.pop_back(); break; } // Else, move on to the next candidate in the list. continue; } tuple_sum += poss; tuple.push_back(poss); subsetSum(possible_lists, p_size, tuple, tuple_sum, subsets, target_sum, p + 1); tuple.pop_back(); tuple_sum -= poss; } } }
TestList() { typedef List<char, DebugAllocator<int> > IntList; IntList a; a.push_back('1'); a.push_back('2'); a.push_back('3'); LIST_ASSERT(*a.begin() == '1'); LIST_ASSERT(*++a.begin() == '2'); LIST_ASSERT(*++++a.begin() == '3'); LIST_ASSERT(++++++a.begin() == a.end()); IntList b; b.push_back('a'); b.push_back('b'); b.push_back('c'); LIST_ASSERT(*b.begin() == 'a'); LIST_ASSERT(*++b.begin() == 'b'); LIST_ASSERT(*++++b.begin() == 'c'); LIST_ASSERT(++++++b.begin() == b.end()); // swap full lists a.swap(b); LIST_ASSERT(*a.begin() == 'a'); LIST_ASSERT(*++a.begin() == 'b'); LIST_ASSERT(*++++a.begin() == 'c'); LIST_ASSERT(++++++a.begin() == a.end()); LIST_ASSERT(*b.begin() == '1'); LIST_ASSERT(*++b.begin() == '2'); LIST_ASSERT(*++++b.begin() == '3'); LIST_ASSERT(++++++b.begin() == b.end()); // swap to/from empty list IntList c; c.swap(b); LIST_ASSERT(b.empty()); LIST_ASSERT(*c.begin() == '1'); LIST_ASSERT(*++c.begin() == '2'); LIST_ASSERT(*++++c.begin() == '3'); LIST_ASSERT(++++++c.begin() == c.end()); c.swap(b); LIST_ASSERT(c.empty()); LIST_ASSERT(*b.begin() == '1'); LIST_ASSERT(*++b.begin() == '2'); LIST_ASSERT(*++++b.begin() == '3'); LIST_ASSERT(++++++b.begin() == b.end()); IntList d; c.swap(d); LIST_ASSERT(c.empty()); LIST_ASSERT(d.empty()); c.splice(c.end(), d); LIST_ASSERT(c.empty()); LIST_ASSERT(d.empty()); // splice full with empty b.splice(b.end(), c); LIST_ASSERT(c.empty()); LIST_ASSERT(*b.begin() == '1'); LIST_ASSERT(*++b.begin() == '2'); LIST_ASSERT(*++++b.begin() == '3'); LIST_ASSERT(++++++b.begin() == b.end()); // splice empty with full c.splice(c.end(), b); LIST_ASSERT(b.empty()); LIST_ASSERT(*c.begin() == '1'); LIST_ASSERT(*++c.begin() == '2'); LIST_ASSERT(*++++c.begin() == '3'); LIST_ASSERT(++++++c.begin() == c.end()); // splice full with full c.splice(c.end(), a); LIST_ASSERT(a.empty()); LIST_ASSERT(*c.begin() == '1'); LIST_ASSERT(*++c.begin() == '2'); LIST_ASSERT(*++++c.begin() == '3'); LIST_ASSERT(*++++++c.begin() == 'a'); LIST_ASSERT(*++++++++c.begin() == 'b'); LIST_ASSERT(*++++++++++c.begin() == 'c'); LIST_ASSERT(++++++++++++c.begin() == c.end()); c.pop_back(); LIST_ASSERT(!c.empty()); c.pop_back(); LIST_ASSERT(!c.empty()); c.pop_back(); LIST_ASSERT(!c.empty()); c.pop_back(); LIST_ASSERT(!c.empty()); c.pop_back(); LIST_ASSERT(!c.empty()); c.pop_back(); LIST_ASSERT(c.empty()); }