本题来自《程序员的算法趣题》中的第5题。
公交车上的零钱兑换机可以将纸币兑换成10日元、50日元、100日元和500日元的硬币,且每种硬币的数量都足够多。因为兑换出太多的硬币不太方便,只允许机器兑换成最多15枚硬币。比如说1000日元的纸币就不能对换成100枚10日元的硬币。
求兑换1000日元纸币会出现多少种不同组合?注意不计硬币兑出的先后顺序(即可以认为硬币是一起出来的)。
这道题目可以表达为如下数学方程的形式:
这是一道线性规划(Linear Programming)问题。
More generally, 令所需要兑换的纸币记为money,可用的硬币以数组形式(降序排列)表示为coins,最多允许的硬币数为maxNum。记f(money, coins, maxNum)表示符合题设要求的组合数。
首先考虑最大面值的硬币coins[0](假定coins中按降序排列)的使用。很显然,coins[0]最少可以用0枚(即不同),最多可以用 枚,据此可以把问题分解为若干个更“小”的子问题来求解。由此可以得到以下递推关系式:
Baseline cases或者说边界条件如下:
- # -*- coding: utf-8 -*-
- """
- Created on Mon Aug 23 08:35:32 2021
-
- @author: chenxy
- """
-
- import sys
- import time
- import random
- from math import gcd, sqrt, ceil
- from typing import List
- # from queue import Queue
-
- class Solution:
-
- def coinChange(self, money:int, coins:List, maxNum: int)->int:
- """
- :money: The money value to be changed into coins
- :coins: Candiate coins in decreasing value order
- :
- :ret: The number of the solutions
- """
- # print('money={0}, coins={1}, maxNum={2}'.format(money,coins,maxNum))
- # Boundary cases
- if money == 0:
- return 1
-
- if len(coins) == 0:
- return 0
-
- # If money is less than the smallest coin, then the change is unsuccessful
- if money < coins[-1]:
- return 0
-
- # Cannot be changed into no larger than maxNum coins
- if ceil(money/coins[0]) > maxNum:
- return 0
-
- nums = 0
- for k in range(money//coins[0]+1):
- nums += self.coinChange(money-k*coins[0], coins[1:], maxNum-k)
-
- return nums
- if __name__ == '__main__':
-
- sln = Solution()
-
- money = 1000
- coins = [500,100,50,10] # In decreasing value order
- maxNum= 15
- tStart = time.time()
- nums = sln.coinChange(money, coins, maxNum)
- tCost = time.time() - tStart
- print('maney = {0}, coins = {1}, nums = {2}, tCost = {3}(sec)'.format(money,coins,nums,tCost))
测试运行结果如下:
money = 1000, coins = [500, 100, 50, 10], nums = 20, tCost = 0.0(sec)
但是以上题解中只给出了硬币兑换方案的种类数,并没有给出具体的兑换方案。应该如何修改程序给出具体的每种兑换方案呢?