はじめに
テンプレは別記事.
A
問題文 : https://atcoder.jp/contests/abc421/tasks/abc421_a
言われた通りに,文字列リストの $x$ 番目の要素が文字列 $y$ と等しいか否かを判定します.型が異なる $x, y$ が同じ行で与えられるせいで,入力をどう扱うかの方が面倒かもしれません.今回は,どちらも String で受け取っておいて使うときになってから read する実装にしました.
main = do n <- readInt ss <- replicateM n readStr [ x, y ] <- readStrs putStrLn $ yesno $ ss !! ( pred $ read x ) == y
B
問題文 : https://atcoder.jp/contests/abc421/tasks/abc421_b
Fibonacci 数列で手前 $2$ 項を足す替わりに手前 $2$ 項を足してから文字列として reverse したものを数として読んだものを次項とする数列の第 $10$ 項を求める問題です.
Haskell で Fibonacci 数列といえば,
as = 1 : 1 : zipWith (+) as ( tail as )
という有名なイディオムがあります.このイディオムにおける (+) を「足してから文字列として reverse したものを数として読む」関数に置き換えれば問題を解けます.
足すのは当然 (+) として,「文字列として reverse して数として読む」部分は read . reverse . show で実装できます.あとはこれらを合成したいですが,(+) が $2$ 引数関数なので read . reverse . show . (+) としてしまうと型が合わず合成できません.ぐぐれば出てくるので詳細は割愛しますが,ここではまず read . reverse . show と (.) の部分適用を取り出して ( ( read . reverse . show ) . ) を作り,これと (+) を合成して ( ( ( read . reverse . show ) . ) . (+) ) とすることで型が合います.
あとは,$10$ 番目の要素を読めば問題が解けます.
main = do [ x, y ] <- readInts let as = x : y : zipWith ( ( ( read . reverse . show ) . ) . (+) ) as ( tail as ) print $ as !! 9
C
問題文 : https://atcoder.jp/contests/abc421/tasks/abc421_c
条件を満たす文字列は ABABA... か BABAB... のいずれかです.また,同じ文字同士の相対位置を逆転させる意味はありません.なので,作る文字列を固定すると各文字の行き先も定まります.一回の操作で行き先までの距離が合計 $2$ 縮まるので,行き先までの距離の総和の半分が,その文字列を作るコストです.よって,$2$ つの目標についてコストを計算して大きくない方をとればそれが答えです.
$2$ 種の目標をできるだけ統一的に扱いたい気がしてきますが,A と B を入れ替えた文字列に対して同じアルゴリズムを適用すればよいです.
各文字の行き先を計算するには,A と B の出現回数をもちながら文字列を先頭から舐めればよいです.
main = do readStr s <- readStr let s' = map ( bool 'A' 'B' . ( == 'A' ) ) s print $ min ( solve s ) ( solve s' ) f _ _ [] = [] f a b (c:s) | c == 'A' = a * 2 : f ( succ a ) b s | otherwise = 1 + b * 2 : f a ( succ b ) s solve = ( `div` 2 ) . sum . map abs . zipWith (-) [ 0 .. ] . f 0 0
D
問題文 : https://atcoder.jp/contests/abc421/tasks/abc421_d
方向と距離からなるリスト $2$ つをパラレルに処理します.リストの先頭にある要素たちを見て,移動距離が大きくない方の距離分の移動を処理して,残り距離が $0$ になったら pop するというようにします.
交わる回数についてはユーザ解説様が全てという気がしますが,
- 同じ方向に動くなら,始点が同じならずっと一緒.
- そうでないなら,$1$ ステップで Manhattan 距離 が $2$ 変化するので交わる「かもしれない」座標が計算可能で,そこに届く移動幅があれば交わる.
ということになります.
あとは,計算に必要な色々な値を計算するコードを書けば問題を解けます.
main = do [ y1, x1, y2, x2 ] <- readInts [ _, m, l ] <- readInts ps <- uncurry ( zipWith (,) ) . ( map head . head &&& map read . last ) . transpose <$> replicateM m readStrs qs <- uncurry ( zipWith (,) ) . ( map head . head &&& map read . last ) . transpose <$> replicateM l readStrs print $ solve y1 x1 y2 x2 ps qs solve _ _ _ _ [] [] = 0 solve y1 x1 y2 x2 ( ( d1, a ):rest1 ) ( ( d2, b ):rest2 ) | d1 == d2 = solve' + if ( y1, x1 ) == ( y2, x2 ) then dist else 0 | odd md || md == 0 = solve' | otherwise = solve' + if ( y1 + dy1 * md `div` 2, x1 + dx1 * md `div` 2 ) == ( y2 + dy2 * md `div` 2, x2 + dx2 * md `div` 2 ) && md `div` 2 <= dist then 1 else 0 where md = abs ( y1 - y2 ) + abs ( x1 - x2 ) dist = min a b directions = zip "RDLU" $ zip [ 0, 1, 0, -1 ] [ 1, 0, -1, 0 ] ( dy1, dx1 ) = fromJust $ lookup d1 directions ( dy2, dx2 ) = fromJust $ lookup d2 directions ny1 = y1 + dy1 * dist nx1 = x1 + dx1 * dist ny2 = y2 + dy2 * dist nx2 = x2 + dx2 * dist ps = if a == dist then rest1 else ( d1, a - dist ):rest1 qs = if b == dist then rest2 else ( d2, b - dist ):rest2 solve' = solve ny1 nx1 ny2 nx2 ps qs