由找零钱说起……(2011-01-11)

作者: Pope怯懦懦地 | 来源:发表于2017-12-09 19:05 被阅读21次

丢人了,Projece Euler 上的第 31 问做了两天都还没搞定。不难啊,可脑子就是想不清楚,唉,老矣。

题目是这样的:

现有面值为1分、2分、5分、10分、20分、50分、1磅(100分)、2磅(200分)的硬币,问2磅钱可以有多少种兑换方案?

这是经典的找零钱问题。

我是这么考虑的,既然要求所有的兑换方案,那我只要把问题规约到这些面额组成的集合的子集上就行了。当我用某一子集来兑换时,该子集上的每种硬币至少含1枚。 我们来看个简化的实例:现有1分、2分和5分三种硬币,兑换10分钱有多少种方法?

  • 当我们去集合{1, 2, 5}时,因为5+2+1已经是 8 了,故只要再加上2的方案,即,5+2+2+1, 5+2+1+1+1
  • 当我们去集合{2, 5}时,因为5+2=7,而 3 不可能用{2, 5}的整数线性组合来得到,故在这种情况下不存在兑换方案;
  • 当我们去集合{1, 5}时,方案为:5+1+1+1+1+1
  • 当我们去集合{1, 2}时,方案为:2+2+2+2+1+1, 2+2+2+1+1+1+1, 2+2+1+1+1+1+1+1, 2+1+1+1+1+1+1+1+1
  • 当我们去集合{5}时,方案为:5+5
  • 当我们去集合{2}时,方案为:2+2+2+2+2
  • 当我们去集合{1}时,方案为:1+1+1+1+1+1+1+1+1+1

共 10 种方案。

递归解法

但其实不用这么麻烦,设可选的硬币面值分别为{S_1, S_2, ..., S_m},用这些面额的硬币来兑换n分钱有count(n, {S_1, S_2, ..., S_m})种方法。有两种不同的兑换方式:

  • 兑换的硬币面额中没有S_m,即count(n, {S_1, S_2, ..., S_{m-1}})
  • 兑换的零钱中至少有一枚S_m分的硬币,即count(n-S_m, {S_1, S_2, ..., S_m})

可我有个疑问,对于第二种情况,count(n-S_m, {S_1, S_2, ..., S_m})真的能代表包含S_m分硬币的所有方案吗?我们需要论证,在只使用{S_1, S_2, ..., S_m}硬币时,含S_m分硬币的n分兑换方案数不比n-S_m分的少。

用反证法,如果n-S_m分的方案数比n分的多,那么在多的那些方案中添上一枚S_m分硬币即可得到新的兑换方案,矛盾。同样,如果n-S_m分的方案数比n分的少,那么在n分兑换方案中多的那些方案中剔除一枚S_m分硬币(这些方案至少有一枚S_m分硬币)即可得到新n-S_m分兑换方案,也矛盾。

这里还隐含了:count(S_m-S_m, {S_1, S_2, ..., S_m}) = 1

还是那个例子:

  1. 1
  2. 2, 1+1
  3. 2+1, 1+1+1
  4. 2+2, 2+1+1, 1+1+1+1
  5. 5, 2+2+1, 2+1+1+1, 1+1+1+1+1
  6. 5+1, 2+2+2, 2+2+1+1, 2+1+1+1+1, 1+1+1+1+1+1
  7. 5+2, 5+1+1, 2+2+2+1, 2+2+1+1+1, 2+1+1+1+1+1, 1+1+1+1+1+1+1
  8. 5+2+1, 5+1+1+1, 2+2+2+2, 2+2+2+1+1, 2+2+1+1+1+1, 2+1+1+1+1+1+1, 1+1+1+1+1+1+1+1
  9. 5+2+2, 5+2+1+1, 5+1+1+1+1, 2+2+2+2+1, 2+2+2+1+1+1, 2+2+1+1+1+1+1, 2+1+1+1+1+1+1+1, 1+1+1+1+1+1+1+1+1
  10. 5+5, 5+2+2+1, 5+2+1+1+1, 5+1+1+1+1+1, 2+2+2+2+2, 2+2+2+2+1+1, 2+2+2+1+1+1+1, 2+2+1+1+1+1+1+1, 2+1+1+1+1+1+1+1+1, 1+1+1+1+1+1+1+1+1+1

以 6 为例,兑换方案分为两组:

一组是6中不含{5}的兑换方案:2+2+2, 2+2+1+1, 2+1+1+1+1, 1+1+1+1+1+1

一组是6 - 5的兑换方案加上51+5

生成函数解法

另一种思路来自生成函数:

要想知道为什么系数就是兑换方案的种数,请参阅:組合數學中的生成函數什么是生成函数?

Mathematica代码:

最后给几个作弊招数:

Length@FrobeniusSolve[{1,2,5,10,20,50,100,200},200] 
Length[IntegerPartitions[200, All, {1, 2, 5, 10, 20, 50, 100, 200}]]  
Length[Reduce[  
  1*a + 2*b + 5*c + 10*d + 20*e + 50*f + 100*g + 200*h == 200 &&   
   a >= 0 && b >= 0 && c >= 0 && d >= 0 && e >= 0 && f >= 0 &&   
   g >= 0 && h >= 0, {a, b, c, d, e, f, g, h}, Integers]]  

P.S. CSDN 的编辑器实在太烂了-_-!

相关文章

  • 由找零钱说起……(2011-01-11)

    丢人了,Projece Euler 上的第 31 问做了两天都还没搞定。不难啊,可脑子就是想不清楚,唉,老矣。 题...

  • 找零的技巧(日更第107天)

    曾经帮亲戚家卖鞭炮的时候,说起了找零的话题,说要是找钱,先找零钱,在找整的,也许顾客没有等到你给他整钱,他就...

  • 背包问题详解

    目录 问题引入 在介绍背包问题之前,我们先来看一个小问题:找零钱问题。 找零钱问题 背包问题的一个浅显版本是找零钱...

  • 【找零钱】

    用python写一个找零钱的算法。 零钱共有50块,20块,10块,5块,和1块,共5种。。例:69 = 50 +...

  • 找零钱

    10人以上 如:男生1元,女生0.5元 围城一圈,中间站一人 主持人:“大家都来找零钱” 大家问:“找多少?” 主...

  • 找零钱

    给定一个数组,数组中为不同的数代表不同钱的面值,同时给定一个需要兑换零钱的钱数,任意使用不同面值不同数量的钱来兑换...

  • 不为人知的破坏财运的习惯,你占了几条?

    ! 说起漏财的习惯,网络上可以找到多如牛毛的搜索结果,内容却大同小异,无外乎不用钱包、到处放钱、不要找零等等这些。...

  • 算法思想之动态规划(三)——找零钱问题

    前言 今天我们继续讨论经典的动态规划问题之找零钱问题。 找零钱问题 问题描述 假设你是一名超市收银员,现有种不同面...

  • 你还算有趣,所以原谅你!

    下午由银行乘出租到学校。 刚上车就听到出租先生温言软语地打电话:"刚才我给人家找零钱,没能好好跟你说话,小乖乖,你...

  • 找零钱问题

    问题 这个题目要求编写一段程序实现统一银座超市的找零方案。只需输入要补给顾客的 金额,然后通过程序就可以计算出该金...

网友评论

    本文标题:由找零钱说起……(2011-01-11)

    本文链接:https://www.haomeiwen.com/subject/ezrpixtx.html