我将此解决方案写入coin change problem on HackerRank:
makeChange :: Int -> [Int] -> Int
makeChange n ys = go n (sort ys)
where go _ [] = 0
go n (x:xs)
| x == n = 1
| x > n = 0
| otherwise = (makeChange n xs) + (makeChange (n - x) (x:xs))
然而,它在一些较大的测试用例上超时。我看到了this关于使用let
绑定实现memoization的文章,但它主要是我的头脑,我不知道如何在这里实现它。有什么帮助吗?
我重新编写了它并获得了显着的性能提升,但我仍然在进行黑客排名练习:
makeChange' :: Int -> [Int] -> Int
makeChange' =
let go _ [] = 0
go n (x:xs)
| x == n = 1
| x > n = 0
| otherwise = (makeChange' n xs) + (makeChange' (n - x) (x:xs))
in go
f (x:y:xs) = makeChange' x ys
where ys = sort xs
main = interact $ show . f . map read . words
我将预排序移动到中间函数f
,我也用它来处理来自Hacker Rank的输入。它们为您提供了一个字符串,其中包含更改目标,更改数组的长度以及更改单元数组。我使用f
从输入中删除长度。
答案 0 :(得分:2)
我将尝试演示behzad.nouri的代码是如何工作的,以便自己理解它:
记住Haskell的懒惰,我们发现!! n
意味着我们的最终列表将被评估为(n+1)
元素。我们对每枚硬币的主要区块是:
let b = (take x a) ++ zipWith (+) b (drop x a) in b
这里有一个小技巧,因为b
是一个列表。请注意,如果b
是整数,那么类似的自引用将不起作用。让我们说我们唯一的硬币是2:
> let b = (take 2 [1,0,0,0,0]) ++ zipWith (+) b (drop 2 [1,0,0,0,0]) in b
=> [1,0,1,0,1]
(这意味着一种方法可以使零,一种方法可以产生两种,一种方式可以产生四种方式。)b
以递归方式构建,因为它是自我引用的,直到有一个方法。拉链评估的长度匹配:
> zipWith (+) [] (drop 2 [1,0,0,0,0])
=> []
-- But we prepended [1,0] so the next `b` is [1,0]
> zipWith (+) [1,0] (drop 2 [1,0,0,0,0])
=> [1,0]
-- But we prepended [1,0] so the next `b` is [1,0,1,0]
> zipWith (+) [1,0,1,0] (drop 2 [1,0,0,0,0])
=> [1,0,1]
-- But we prepended [1,0] so the result matching the length is [1,0,1,0,1]
现在让我们使用这些知识来完成solve 4 [1,2,3]
:
let b = (take 3 [1,0,0,0,0]) ++ zipWith (+) b (drop 3 [1,0,0,0,0]) in b
take 3 [1,0,0,0,0] -- n+1 elements are evaluated from the infinite list
=> [1,0,0]
++ [0,0] -- drop 3 from [1,0,0,0,0]
(+) []
=> []
-- prepend [1,0,0]
=> [1,0,0]
++ [0,0]
(+) [1,0,0]
=> [1,0]
-- prepend [1,0,0]
=> [1,0,0,1,0]
这是制造零的一种方式,也是制造三种方式的一种方式。下一个:
let b = (take 2 [1,0,0,1,0]) ++ zipWith (+) b (drop 2 [1,0,0,1,0]) in b
take 2 [1,0,0,1,0]
=> [1,0]
++ [0,1,0]
(+) []
=> []
-- prepend [1,0]
=> [1,0]
++ [0,1,0]
(+) [1,0]
=> [1,1,0]
-- prepend [1,0]
=> [1,0,1,1,0]
++ [0,1,0]
(+) [1,0,1,1,0]
=> [1,1,1]
-- prepend [1,0]
=> [1,0,1,1,1]
这是制作0的一种方法,一种制作2的方法,一种制作3的方法,以及一种制作方法4.下一步:
let b = (take 1 [1,0,1,1,1]) ++ zipWith (+) b (drop 1 [1,0,1,1,1]) in b
这是迭代次数最多的一次,因为b
仅由一个元素构建
take 1 [1,0,1,1,1]
=> [1]
++ [0,1,1,1]
(+) []
=> []
-- prepend [1]
=> [1]
++ [0,1,1,1]
(+) [1]
=> [1]
-- prepend [1]
=> [1,1]
++ [0,1,1,1]
(+) [1,1]
=> [1,2]
-- prepend [1]
=> [1,1,2]
++ [0,1,1,1]
(+) [1,1,2]
=> [1,2,3]
-- prepend [1]
=> [1,1,2,3]
++ [0,1,1,1]
(+) [1,1,2,3]
=> [1,2,3,4]
-- prepend [1]
=> [1,1,2,3,4]
这是制作0的1种方法,1种制作1种方法,2种制作2种方法,3种方式制作3种方法以及4种方式制作4种方式。