硬币数量有限的硬币兑换算法 - 方案
Coin Change Algorithm with limited coins - Scheme
我需要编写程序 change 来获取一笔钱和可用硬币的列表,以及
returns 多种可能的方式 用这些硬币支付钱。
正如我所说,硬币是有限的。
示例:
(change 10 '(5 5 10)) → 2
;; [1 coin of 10 ,2 coins of 5]
(change 5 '(1 1 1 2 2 5 10)) → 3
;; [1 coin of 5, 1 coin of 2 and 3 coins of 1, 2 coins of 2 and 1 coin of 1]
我试过这个:
(define (change sum coins)
(if (< sum 0)
0
(if (= sum 0)
1
(if (and (> sum 0) (<= (length coins) 0))
0
(+ (change (- sum (car coins)) (cdr coins))
(change sum (cdr coins)))))))
但我无法摆脱一些重复计数...
第二个示例的输出不是 3,而是:
(change 5 '(1 1 1 2 2 5 10)) → 6
如何解决这个问题?
(Racket 中的代码应该是 运行,纯函数式,不使用任何内置方法。我更喜欢递归。唯一允许的数据结构是 List 和 Pair)
:
ways( 0 , _ ) = 1
ways( sum, _ ) | sum < 0 = 0
ways( sum, 0 ) | sum > 0 = 0
ways( sum, k ) = ways( sum, k - 1 )
+
ways( sum - first_denomination(k), k )
也就是
= ways( sum, k - 1 )
+
ways( sum - first_denomination(k), k - 1 )
+
ways( sum - 2*first_denomination(k), k )
= ......
......
= ways( sum, k - 1 )
+
ways( sum - first_denomination(k), k - 1 )
+
ways( sum - 2*first_denomination(k), k - 1 )
+
ways( sum - 3*first_denomination(k), k - 1 )
+
......
(直到基本情况)。因此,有限的硬币找零是
ways( sum, [ [c, n], ...t] ) =
ways( sum, t ) ; taking none of c
+
ways( sum - c, t ) ; taking one of c
+
ways( sum - 2*c, t ) ; taking two of c
+
...
+
ways( sum - n*c, t ) ; taking n c-valued coins
使用适当调整的基本案例。
剩下的就是用 Scheme 语法写下这个东西,这样你就可以称它为
(change 5 '((1 3) (2 2) (5 1) (10 1)) ) → 3
完成!
我的算法很简单,使用这个 remove-all
方法:
(define remove-all
(lambda (x lst)
(if (empty? lst)
'()
(if (= (car lst) x)
(remove-all x (cdr lst))
(cons (car lst)
(remove-all x (cdr lst)))))))
(define payment
(lambda (n coins-lst)
(if (< n 0)
0
(if (= n 0)
1
(if (and (> n 0) (empty? coins-lst))
0
(+ (payment (- n (car coins-lst)) (cdr coins-lst))
(payment n (remove-all (car coins-lst) coins-lst))))))))
谢谢你的建议!
我需要编写程序 change 来获取一笔钱和可用硬币的列表,以及 returns 多种可能的方式 用这些硬币支付钱。 正如我所说,硬币是有限的。
示例:
(change 10 '(5 5 10)) → 2
;; [1 coin of 10 ,2 coins of 5]
(change 5 '(1 1 1 2 2 5 10)) → 3
;; [1 coin of 5, 1 coin of 2 and 3 coins of 1, 2 coins of 2 and 1 coin of 1]
我试过这个:
(define (change sum coins)
(if (< sum 0)
0
(if (= sum 0)
1
(if (and (> sum 0) (<= (length coins) 0))
0
(+ (change (- sum (car coins)) (cdr coins))
(change sum (cdr coins)))))))
但我无法摆脱一些重复计数... 第二个示例的输出不是 3,而是:
(change 5 '(1 1 1 2 2 5 10)) → 6
如何解决这个问题?
(Racket 中的代码应该是 运行,纯函数式,不使用任何内置方法。我更喜欢递归。唯一允许的数据结构是 List 和 Pair)
ways( 0 , _ ) = 1
ways( sum, _ ) | sum < 0 = 0
ways( sum, 0 ) | sum > 0 = 0
ways( sum, k ) = ways( sum, k - 1 )
+
ways( sum - first_denomination(k), k )
也就是
= ways( sum, k - 1 )
+
ways( sum - first_denomination(k), k - 1 )
+
ways( sum - 2*first_denomination(k), k )
= ......
......
= ways( sum, k - 1 )
+
ways( sum - first_denomination(k), k - 1 )
+
ways( sum - 2*first_denomination(k), k - 1 )
+
ways( sum - 3*first_denomination(k), k - 1 )
+
......
(直到基本情况)。因此,有限的硬币找零是
ways( sum, [ [c, n], ...t] ) =
ways( sum, t ) ; taking none of c
+
ways( sum - c, t ) ; taking one of c
+
ways( sum - 2*c, t ) ; taking two of c
+
...
+
ways( sum - n*c, t ) ; taking n c-valued coins
使用适当调整的基本案例。
剩下的就是用 Scheme 语法写下这个东西,这样你就可以称它为
(change 5 '((1 3) (2 2) (5 1) (10 1)) ) → 3
完成!
我的算法很简单,使用这个 remove-all
方法:
(define remove-all
(lambda (x lst)
(if (empty? lst)
'()
(if (= (car lst) x)
(remove-all x (cdr lst))
(cons (car lst)
(remove-all x (cdr lst)))))))
(define payment
(lambda (n coins-lst)
(if (< n 0)
0
(if (= n 0)
1
(if (and (> n 0) (empty? coins-lst))
0
(+ (payment (- n (car coins-lst)) (cdr coins-lst))
(payment n (remove-all (car coins-lst) coins-lst))))))))
谢谢你的建议!