I'm only asking for a description (pseudocode) for a better method.
allSuitablePayments (int price, const std::array<int,N>& coinsPossessed)
is a function that is to return a list of all possible std::array<int,N>'s
that represent the amounts of each coin being spent to purchase an item whose price is 'price'. The rule, however, is that
no redundant coin be spent.
For example, if 4 Zlots are enough to purchase an item, then a 5th Zlot shall not be given to the cashier (nor shall any other coin type be given on top of the 4 Zlots).
The pack 'CoinValues...' is the value of each coin, and the elements of the array 'coinsPossessed' represents how much of each coin the customer has (the coin types being in the same order as 'CoinValues...').
Here is an example. Suppose <CoinValues...> is <1,5,10,25> and coinsPossessed = {5,2,3,4}. This means you have 5 pennies, 2 nickels, 3 dimes, and 4 quarters. Suppose you want to buy an item whose price is 54 cents. Then some possible payments allowed are:
- 3 quarters
- 2 quarters, 1 nickel
- 2 quarters, 4 pennies
- 1 quarter, 3 dimes,
etc...
and this would be outputted by 'allSuitablePayments' as
{ {0,0,0,3}, {0,1,0,2}, {4,0,0,2}, {0,0,3,1}, ...}
My solution below is far too slow, because I first generate ALL possible payments, and then I remove all payments that either are short in change, or has a redundant coin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
template <std::size_t N>
std::list<std::array<int, N>> generateArraysHelper (std::list<std::array<int, N>>& all, const std::array<int, N>& limits, std::size_t index) {
std::list<std::array<int, N>> newArrays;
for (std::array<int, N>& a : all)
for (int i = 0; i <= limits[index]; i++) { // Spawn first+1 new arrays.
std::array<int, N> newA = a;
newA[index] = i;
newArrays.emplace_back(newA);
}
if (index == N - 1)
return newArrays;
return generateArraysHelper<N>(newArrays, limits, index + 1);
}
// Returns a list of all array<int, N>'s with each component ranging from 0 to the corresponding value specified in 'limits'.
// For example generateArrays<5>({2,2,2,2,2}) will yield the list of all array<int, 5>'s with each component being 0, 1, or 2.
template <std::size_t N>
std::list<std::array<int, N>> generateArrays (const std::array<int, N>& limits) {
std::list<std::array<int, N>> all = { {0} };
return generateArraysHelper<N> (all, limits, 0);
}
// Take ALL possible array values for coinsPaid. Remove those that total up less than 'price' and remove those that don't meet the
// the main condition of the problem (i.e. has a redundant coin in the sense that if a coin is removed, it is still totals up greater than 'price').
template <int... CoinValues>
std::list<std::array<int, sizeof...(CoinValues)>> allSuitablePayments (int price, const std::array<int, sizeof...(CoinValues)>& coinsPossessed) {
constexpr std::size_t N = sizeof...(CoinValues);
std::list<std::array<int, N>> candidates = generateArrays<N>(coinsPossessed);
// Now we remove from 'candidates' all arrays that fail to meet the value 'price' or has a redundant coin.
candidates.remove_if ([price](const std::array<int, N>& x)->bool {
if (totalAmount<N, CoinValues...>(x) < price)
return true; // Since x fails to be enough to pay for the item with value 'price'.
for (std::size_t i = 0; i < N; i++) {
if (x[i] == 0)
continue;
std::array<int, N> a = x;
a[i]--; // Remove one coin of the ith type.
if (totalAmount<N, CoinValues...>(a) >= price)
return true; // Removing one coin of the ith type still yields an amount >= price, so that ith coin is redundant. This means that x has a redundant coin.
}
return false;
});
return candidates;
}
int main() {
const std::list<std::array<int, 5>> suitablePayments = allSuitablePayments<1,10,25,50,100>(151, {30,4,5,2,1}); // 37 possibilities
}
|
The function totalAmount I did not post above because I don't want to clutter the ideas with other functions (but if you want it I can post that too). The reason I need to improve the time complexity is because in my main program, the coins possessed are usually in the hundreds (or more).