关于树:项目Euler#18在Haskell

Project Euler #18 in Haskell

我目前正在学习函数式编程,Haskell来自Python背景。为了帮助我学习,我决定做一些ProjectEuler问题(http://projectEuler.net/problem=18)。我现在18岁。从这个字符串开始,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23"

我使用以下函数将它转换为嵌套数组:

1
map (\x -> map (\y -> read y :: Int) (words x)) (lines a)

该函数输出:

1
[[75],[95,64],[17,47,82],[18,35,87,10],[20,4,82,47,65],[19,1,23,75,3,34],[88,2,77,73,7,63,67],[99,65,4,28,6,16,70,92],[41,41,26,56,83,40,80,70,33],[41,48,72,33,47,32,37,16,94,29],[53,71,44,65,25,43,91,52,97,51,14],[70,11,33,28,77,73,17,78,39,68,17,57],[91,71,52,38,17,14,91,43,58,50,27,29,48],[63,66,4,68,89,53,67,30,73,16,69,87,40,31],[4,62,98,27,23,9,70,98,73,93,38,53,60,4,23]]
  • 有没有一种方法可以在没有lambda和make的情况下编写转换函数它更可读?
  • 如何将此嵌套数组转换为树结构定义如下:

    数据树A=空树节点A(树A)(树A)派生(显示、读取、eq)

    或者将它作为一个数组保存并从那里解决它会更容易吗?


  • 这是我想到的

    1
    2
    foo :: String -> [[Int]]
    foo = map (map read) . map words . lines

    可以使用递归定义从中构造树。

    1
    2
    3
    4
    5
    6
    7
    fromList :: [[a]] -> Tree a
    fromList [[]] = EmptyTree
    fromList [[x]] = Node x EmptyTree EmptyTree
    fromList ([x]:rs) = Node x ltree rtree
      where ltree = fromList . snd $ mapAccumL (
     l -> (n+1,take n l)) 1 rs
            rtree = fromList $ map (drop 1) rs


    您可以使用foldr将列表列表转换为Tree

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    -- given the elements for the current generation of trees,
    -- and a list of trees in the next generation, generate
    -- the current generation of trees
    -- assumes |as| == |ts| - 1
    generation :: [a] -> [Tree a] -> [Tree a]
    generation as ts = zipWith3 Node as (init ts) (tail ts)

    -- forest gs ~ generates |head gs| trees
    forest :: [[a]] -> [Tree a]
    forest = foldr generation $ repeat EmptyTree

    fromList :: [[a]] -> Maybe (Tree a)
    fromList gs = case forest gs of
      [t] -> Just t
      _   -> Nothing

    请注意,在上一代中,同一棵树如何被重用为两个不同树的子树。

    倒着思考可能会有帮助。

    • 最后一行中的每个元素都将得到一个EmptyTree,作为左、右子元素。

      1
      2
      3
      4
      5
      6
      generation as ts = zipWith3 Node as (init ts) (tail ts)
        as = [4,62,98,27,23,9,70,98,73,93,38,53,60,4,23]
        ts = [EmptyTree, EmptyTree, ..]
        init ts = [EmptyTree, EmptyTree, ..]
        tail ts = [EmptyTree, EmptyTree, ..]
        zipWith3 Node as (init ts) (tail ts) = [Node 4 EmptyTree EmptyTree,Node 62 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 27 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree,Node 9 EmptyTree EmptyTree,Node 70 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 73 EmptyTree EmptyTree,Node 93 EmptyTree EmptyTree,Node 38 EmptyTree EmptyTree,Node 53 EmptyTree EmptyTree,Node 60 EmptyTree EmptyTree,Node 4 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree]
    • 最后一行旁边的每个元素都使用最后一行的树

      1
      2
      3
      4
      5
      6
      generation as ts = zipWith3 Node as (init ts) (tail ts)
        as = [63,66,4,68,89,53,67,30,73,16,69,87,40,31]
        ts = [Node 4 EmptyTree EmptyTree,Node 62 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 27 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree,Node 9 EmptyTree EmptyTree,Node 70 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 73 EmptyTree EmptyTree,Node 93 EmptyTree EmptyTree,Node 38 EmptyTree EmptyTree,Node 53 EmptyTree EmptyTree,Node 60 EmptyTree EmptyTree,Node 4 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree]
        init ts = [Node 4 EmptyTree EmptyTree,Node 62 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 27 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree,Node 9 EmptyTree EmptyTree,Node 70 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 73 EmptyTree EmptyTree,Node 93 EmptyTree EmptyTree,Node 38 EmptyTree EmptyTree,Node 53 EmptyTree EmptyTree,Node 60 EmptyTree EmptyTree,Node 4 EmptyTree EmptyTree]
        tail ts = [Node 62 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 27 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree,Node 9 EmptyTree EmptyTree,Node 70 EmptyTree EmptyTree,Node 98 EmptyTree EmptyTree,Node 73 EmptyTree EmptyTree,Node 93 EmptyTree EmptyTree,Node 38 EmptyTree EmptyTree,Node 53 EmptyTree EmptyTree,Node 60 EmptyTree EmptyTree,Node 4 EmptyTree EmptyTree,Node 23 EmptyTree EmptyTree]
        zipWith3 Node as (init ts) (tail ts) = [Node 63 (Node 4 EmptyTree EmptyTree) (Node 62 EmptyTree EmptyTree),Node 66 (Node 62 EmptyTree EmptyTree) (Node 98 EmptyTree EmptyTree),Node 4 (Node 98 EmptyTree EmptyTree) (Node 27 EmptyTree EmptyTree),Node 68 (Node 27 EmptyTree EmptyTree) (Node 23 EmptyTree EmptyTree),Node 89 (Node 23 EmptyTree EmptyTree) (Node 9 EmptyTree EmptyTree),Node 53 (Node 9 EmptyTree EmptyTree) (Node 70 EmptyTree EmptyTree),Node 67 (Node 70 EmptyTree EmptyTree) (Node 98 EmptyTree EmptyTree),Node 30 (Node 98 EmptyTree EmptyTree) (Node 73 EmptyTree EmptyTree),Node 73 (Node 73 EmptyTree EmptyTree) (Node 93 EmptyTree EmptyTree),Node 16 (Node 93 EmptyTree EmptyTree) (Node 38 EmptyTree EmptyTree),Node 69 (Node 38 EmptyTree EmptyTree) (Node 53 EmptyTree EmptyTree),Node 87 (Node 53 EmptyTree EmptyTree) (Node 60 EmptyTree EmptyTree),Node 40 (Node 60 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree),Node 31 (Node 4 EmptyTree EmptyTree) (Node 23 EmptyTree EmptyTree)]

      zipWith3 f as bs cs只是在步骤中从每个列表中提取元素,并将给定的函数应用于具有相同索引的元素,从而得到[ f a0 b0 c0, f a1 b1 c1, ... ]

      您也可以使用forest [[63,66,4,68,89,53,67,30,73,16,69,87,40,31],[4,62,98,27,23,9,70,98,73,93,38,53,60,4,23]]直接计算

    您是对的,您不需要生成这个中间数据结构来解决这个问题,但是您可以使用这个相同的结构来直接计算您的答案。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    generation :: Num a => [a] -> [(a, [a])] -> [(a, [a])]
    generation as ts = zipWith3 ????  as (init ts) (tail ts)

    forest :: Num a => [[a]] -> [(a, [a])]
    forest = foldr generation $ repeat ????

    fromList :: [[a]] -> Maybe (a, [a])
    fromList gs = case forest gs of
      [t] -> Just t
      _   -> Nothing

    -- what's the maximum total sum of any path
    maxTotal :: [[a]] -> Maybe a
    maxTotal = fmap fst . fromList

    -- what's the path with maximum total sum
    maxPath :: [[a]] -> Maybe [a]
    maxPath = fmap snd . fromList


    我喜欢嵌套列表,也许是因为我对树不太了解…这里有一个提示:

    如果你从最长的一行开始用n个数字(即n个选项),你能把它转换成一行n-1个数字/选项,每个数字/选项都对应于前面一行的数字/选项吗?

    (使用嵌套列表作为参数,可以使用haskell在一行中对解决方案进行编码)