考え方
それぞれの値に座標をつけることで、縦横斜めに走査する
例 以下のグリッドを考える
A B C D
E F G H
I J K L
M N O P
1. 横に見る
[[A, B, C, D], [E, F, G, H], [I, J, K, L],[M, N, O, P]]
2. 縦に見る
[[A, E, I, M], [B, F, J, N], [C, G, K, O], [D, H, L, P]]
3. 右斜め下に見る
[[M], [I, N], [E, J, O], [A, F, K, P], [B, G, L], [D]]
4. 左斜め下に見る
[[A], [B, E], [C, F, I], [D, G, J, M], [H, K, N], [L, O], [P]]
これらのリストを連結してそれぞれのリストの中の最大積を求め、一番大きい値が答えになる
座標をつける
型に名前をつける
-- 座標 (x, y)
type Cell = (Int, Int)
-- 与えられた値に座標をつけたもの
type Grid = [(Cell, Int)]
先に[(0, 0), (0, 1)..]のような座標のリストを作ることでzipで簡単に作成できる
cells :: [Cell]
cells = [(x, y) | y <- [0..19], x <- [0..19]]
grid :: Grid
grid = zip cells $ map read (words gridString)
-- 与えられた数字の表
gridString :: String
gridString = "08 02 22 97 ~ 67 48"
4パターンのリストを作りたい
1. 横用のリストを作る
y座標が同じもので(横に)グルーピング
2. 縦用のリストを作る
x座標が同じもので(縦に)グルーピング
3. 右斜め下用のリストを作る
A B C D
E F G H ← x座標を1つマイナス
I J K L ←← x座標を2つマイナス
M N O P ←←← x座標を3つマイナス
こうずらしてx座標が同じもので(縦に)グルーピングすれば良い
→
x座標+y座標が同じもの同士でグルーピングすれば良いと言うこと
4. 左斜め下用のリストを作る
3と同様に考え、
x座標−y座標が同じもの同士でグルーピングすれば良い
-- 4パターンに応じた型を定義する
data Coordinates = Horizon | Vertical | LowerR | LowerL
-- 指定した座標値を返す関数
select :: Coordinates -> (Int, Int) -> Int
select Horizon (x, y) = y
select Vertical (x, y) = x
select LowerR (x, y) = x + y
select LowerL (x, y) = x - y
同じ値でグルーピングするには、ソートして連続する値をまとめればいい
getRows :: Coordinates -> [((Int, Int), Int)] -> [[Int]]
getRows c = map (map snd) . groupBy (\(cell,_) (cell2, _) -> select c cell == select c cell2) . sortOn (\(cell, _) -> select c cell)
最後に適用しているmap (map snd)は座標の値は必要ないので捨てたかっただけ
リスト作成
-- 横で見る
horizon :: [[Int]]
horizon = getRows Horizon grid
-- 縦で見る
vertical :: [[Int]]
vertical = getRows Vertical grid
-- 右下に斜めで見る
lowerR :: [[Int]]
lowerR = getRows LowerR grid
-- 左下に斜めで見る
lowerL :: [[Int]]
lowerL = getRows LowerL grid
別にまとめて作ればいいのだが、わかりやすさのため
リストの中の連続する4つの数値での最大の積を取得する関数
簡潔に済ます
getMax :: [Int] -> Int
getMax xs = maximum $ map (product . take 4) $ tails xs