Q3 Make Change
题目描述
mplement make_change
, which takes a positive integer amount
and a dictionary of coins
. The coins
dictionary keys are positive integer denominations and its values are positive integer coin counts. For example, {1: 4, 5: 2}
represents four pennies and two nickels. The make_change
function returns a list of coins that sum to amount
, where the count of any denomination k
in the return value is at most coins[k]
.
If there are multiple ways to make change for amount
, prefer to use as many of the smallest coins available and place the smallest coins first in the returned list.
def make_change(amount, coins):
"""Return a list of coins that sum to amount, preferring the smallest coins
available and placing the smallest coins first in the returned list.
The coins argument is a dictionary with keys that are positive integer
denominations and values that are positive integer coin counts.
>>> make_change(2, {2: 1})
[2]
>>> make_change(2, {1: 2, 2: 1})
[1, 1]
>>> make_change(4, {1: 2, 2: 1})
[1, 1, 2]
>>> make_change(4, {2: 1}) == None
True
>>> coins = {2: 2, 3: 2, 4: 3, 5: 1}
>>> make_change(4, coins)
[2, 2]
>>> make_change(8, coins)
[2, 2, 4]
>>> make_change(25, coins)
[2, 3, 3, 4, 4, 4, 5]
>>> coins[8] = 1
>>> make_change(25, coins)
[2, 2, 4, 4, 5, 8]
"""
if not coins:
return None
smallest = min(coins)
rest = remove_one(coins, smallest)
if amount < smallest:
return None
"*** YOUR CODE HERE ***"
You should use the remove_one
function in your implementation:
def remove_one(coins, coin):
"""Remove one coin from a dictionary of coins. Return a new dictionary,
leaving the original dictionary coins unchanged.
>>> coins = {2: 5, 3: 2, 6: 1}
>>> remove_one(coins, 2) == {2: 4, 3: 2, 6: 1}
True
>>> remove_one(coins, 6) == {2: 5, 3: 2}
True
>>> coins == {2: 5, 3: 2, 6: 1} # Unchanged
True
"""
copy = dict(coins)
count = copy.pop(coin) - 1 # The coin denomination is removed
if count:
copy[coin] = count # The coin denomination is added back
return copy
分析
题目要求make_change函数实现找零钱的功能,接收金额amount和字典coins,返回字典coins中记录的各面值硬币凑出amount值的组合的列表(要求该硬币组合尽量用最多面值最小的硬币,且在列表中将面值小的硬币放在前面)。
题目给出了一个remove_one函数,接收两个参数:一个字典coins记录各面值硬币数量,一个coin为要从coins删除的一个硬币的面值。该函数首先复制coins字典,然后将copy中关键字为coin的元素弹出,使用count记录弹出一个该面值硬币后剩下的硬币数量,如果弹出一个后剩余的该面值硬币数量不为0,那么将弹出后的键值对再加入复制的字典中,最后返回该复制的字典(即原coins字典不变,返回一个弹出对应面值硬币后的字典)。
实现make_change函数可以直观地使用递归思想实现,我们应当假设每个make_change函数都实现了使用coins完成对amount金额的找零(包括成功和失败的情景,成功会返回找零的硬币的列表,失败会返回None)。
首先考虑basecase的情况,如果coins为空,那么无法对amount进行找零,返回None;如果coins不为空,用smallest记录当前coins中最小面额(smallest = min(coins)),并用rest记录弹出一个smallest面值的货币后的coins以便后续递归过程使用,此时有两种情况:amount金额小于当前coins中最小面值的硬币,那么也无法完成找零,返回None;如果amount金额等于当前coins中最小面值的硬币,则完成了找零,返回[smallest]。
然后考虑amount金额大于当前coins中最小面额的情况,用result记录使用了这个smallest硬币后的结果(result = make_change(amount - smallest, rest)),此时分为两种情况:一种是最终完成找零的硬币列表需要用到这个smallest面额的硬币,那么result是一个列表而不是None,则当前make_change函数返回[smallest] + result;第二种是完成找零的硬币不需要使用这个smallest面额的硬币,那么返回make_change(amount, rest)(即找零金额不变,但是把这个smallest面值的硬币排除在可选范围之外了,这样如果用不到这个面值的硬币了,就会在后续递归过程中逐渐将这个面值的硬币从coins中清空,满足了尽量使用更小面值的硬币来找零的要求),如果到最后无法完成找零,得到的返回值会是None,如果完成了,得到的返回值会是一个列表。
代码实现
def make_change(amount, coins):
if not coins:
return None
smallest = min(coins)
rest = remove_one(coins, smallest)
if amount < smallest:
return None
if amount == smallest:
return [smallest]
result = make_change(amount - smallest, rest)
if result == None:
return make_change(amount, rest)
else:
return [smallest] + result
Q4: Change Machine
题目描述
Complete the change
method of the ChangeMachine
class. A ChangeMachine
instance holds some coins
, which are initially all pennies. The change
method takes a positive integer coin
, adds that coin to its coins
, and then returns a list that sums to coin
. The machine prefers to return as many of the smallest coins available, ordered from smallest to largest. The coins returned by change
are removed from the machine's coins
.
class ChangeMachine:
"""A change machine holds a certain number of coins, initially all pennies.
The change method adds a single coin of some denomination X and returns a
list of coins that sums to X. The machine prefers to return the smallest
coins available. The total value in the machine never changes, and it can
always make change for any coin (perhaps by returning the coin passed in).
The coins attribute is a dictionary with keys that are positive integer
denominations and values that are positive integer coin counts.
>>> m = ChangeMachine(2)
>>> m.coins == {1: 2}
True
>>> m.change(2)
[1, 1]
>>> m.coins == {2: 1}
True
>>> m.change(2)
[2]
>>> m.coins == {2: 1}
True
>>> m.change(3)
[3]
>>> m.coins == {2: 1}
True
>>> m = ChangeMachine(10) # 10 pennies
>>> m.coins == {1: 10}
True
>>> m.change(5) # takes a nickel & returns 5 pennies
[1, 1, 1, 1, 1]
>>> m.coins == {1: 5, 5: 1} # 5 pennies & a nickel remain
True
>>> m.change(3)
[1, 1, 1]
>>> m.coins == {1: 2, 3: 1, 5: 1}
True
>>> m.change(2)
[1, 1]
>>> m.change(2) # not enough 1's remaining; return a 2
[2]
>>> m.coins == {2: 1, 3: 1, 5: 1}
True
>>> m.change(8) # cannot use the 2 to make 8, so use 3 & 5
[3, 5]
>>> m.coins == {2: 1, 8: 1}
True
>>> m.change(1) # return the penny passed in (it's the smallest)
[1]
>>> m.change(9) # return the 9 passed in (no change possible)
[9]
>>> m.coins == {2: 1, 8: 1}
True
>>> m.change(10)
[2, 8]
>>> m.coins == {10: 1}
True
>>> m = ChangeMachine(9)
>>> [m.change(k) for k in [2, 2, 3]]
[[1, 1], [1, 1], [1, 1, 1]]
>>> m.coins == {1: 2, 2: 2, 3: 1}
True
>>> m.change(5) # Prefers [1, 1, 3] to [1, 2, 2] (more pennies)
[1, 1, 3]
>>> m.change(7)
[2, 5]
>>> m.coins == {2: 1, 7: 1}
True
"""
def __init__(self, pennies):
self.coins = {1: pennies}
def change(self, coin):
"""Return change for coin, removing the result from self.coins."""
"*** YOUR CODE HERE ***"
分析
题目要求实现一个ChangeMachine类的change方法,该方法用于实现找零功能。每个ChangeMachine类的实例都有一个coins属性用于记录当前拥有的各面值硬币数量,如果coins中的硬币能满足找零要求,则返回零钱列表,在coins中删除找出的零钱,加入被找零的面值的硬币。
change方法的实现使用Q3中完成的make_change函数,需要注意的是不同情况的处理:如果能找开,就返回找零的列表,如果找不开,就返回投入的货币,change方法在执行过程中要完成对coins属性的修改。那么可以在一开始就将要找零的货币coin加入coins属性中(这里要用到字典类的get方法,该方法接受两个参数,第一个参数为目标key值,第二个参数为找不到目标key值情况下返回的default值),以确保使用make_change函数能够找到一个找零方案,然后在返回这个方案的列表之前根据这个列表从coins属性中删除相应的硬币。
代码实现
class ChangeMachine:
"""A change machine holds a certain number of coins, initially all pennies.
The change method adds a single coin of some denomination X and returns a
list of coins that sums to X. The machine prefers to return the smallest
coins available. The total value in the machine never changes, and it can
always make change for any coin (perhaps by returning the coin passed in).
The coins attribute is a dictionary with keys that are positive integer
denominations and values that are positive integer coin counts.
"""
def __init__(self, pennies):
self.coins = {1: pennies}
def change(self, coin):
"""Return change for coin, removing the result from self.coins."""
self.coins[coin] = self.coins.get(coin, 0) + 1
result = make_change(coin, self.coins)
for key in result:
self.coins = remove_one(self.coins, key)
return result