丟人了,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
-
2
,1+1
-
2+1
,1+1+1
-
2+2
,2+1+1
,1+1+1+1
-
5
,2+2+1
,2+1+1+1
,1+1+1+1+1
-
5+1
,2+2+2
,2+2+1+1
,2+1+1+1+1
,1+1+1+1+1+1
-
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
-
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
-
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
-
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
的兌換方案加上5
:1+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 的編輯器實在太爛了-_-!