-
Notifications
You must be signed in to change notification settings - Fork 21
/
make_change.rb
57 lines (46 loc) · 1.79 KB
/
make_change.rb
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
49
50
51
52
53
54
55
56
57
# dynamic programming version
def make_change(amount, coins)
result = Array.new(amount + 1, 0)
result[0] = 1
coins.each do |coin|
(coin..amount).each do |higher_amount|
remainder = higher_amount - coin
result[higher_amount] += result[remainder]
end
end
result[amount]
end
# recursive version:
def make_change2(target, coins = [25, 10, 5, 1])
return [] if target == 0
return nil if coins.none? { |coin| coin <= target }
coins = coins.sort.reverse
best_change = nil
coins.each_with_index do |coin, index|
# can't use this coin, it's too big
next if coin > target
# use this coin
remainder = target - coin
# Find the best way to make change with the remainder (recursive
# call). Why `coins.drop(index)`? This is an optimization. Because
# we want to avoid double counting; imagine two ways to make
# change for 6 cents:
# (1) first use a nickel, then a penny
# (2) first use a penny, then a nickel
# To avoid double counting, we should require that we use *larger
# coins first*. This is what `coins.drop(index)` enforces; if we
# use a smaller coin, we can never go back to using larger coins
# later.
best_remainder = make_change2(remainder, coins.drop(index))
# We may not be able to make the remaining amount of change (e.g.,
# if coins doesn't have a 1cent piece), in which case we shouldn't
# use this coin.
next if best_remainder.nil?
# Otherwise, the best way to make the change **using this coin**,
# is the best way to make the remainder, plus this one coin.
this_change = [coin] + best_remainder
# Is this better than anything we've seen so far?
best_change = this_changeif best_change.nil? || this_change.count < best_change.count
end
best_change
end