AtCoder Beginner Contest 328 振り返り

今週も振り返り。

結果

C問題がTLEで解けずに、今回はA, Bの2完でした。

レートはほぼ変わらずです。

各問題の振り返り

A,Bは解けましたが、CがTLEになってしまいました。

A - Not Too Hard

リストからある値以下の値を抜き出して合計する問題。
問題名の通り、それほど難しくはなかったです。filterして合計値を算出することで算出できました。

main :: IO ()
main = do
  [_, x] <- readInputIntList
  ss <- readInputIntList

  print $ sum $ filter (<= x) ss

B - 11/11

各月の月と日付のうち、ゾロ目になっている個数を算出する問題です。月と日数は標準入力で与えられます。
各月ごとの日数の配列を作っておいて、文字列にして全ての文字が同じであることを判定する(all (head list) list)と解けます。リスト内包表記が便利なのですが、いまいち使い慣れておらず時間がかかってしまいました。

main :: IO ()
main = do
  _ <- readInputInt
  ss <- readInputIntList

  print $ countZoromeDays ss

isZorome :: Int -> Int -> Bool
isZorome m d = all (== head str) str
  where
    str = show m ++ show d

countZoromeDays :: [Int] -> Int
countZoromeDays daysPerMonth = sum [1 | m <- [1 .. length daysPerMonth], d <- [1 .. daysPerMonth !! (m - 1)], isZorome m d]

C - Consecutive

ある範囲の文字列から、隣り合う文字が重複している個数を算出する問題です。最初はあらかじめ計算して重複する文字列のインデックスをリストに持っておけば解けると思っていたのですが、このやり方だと毎回重複する文字数を計算することになるので、TLEになってしまいます。(計算量はO(n^2)になるはず。)

main :: IO ()
main = do
  [_, q] <- readInputIntList
  ss <- getLine
  lrs <- replicateM q readPairInt

  let adjacentIdxs = findAdjacentChars ss

  mapM_ (\(l, r) -> print (length (filter (\idx -> idx >= l && idx < r) adjacentIdxs))) lrs

findAdjacentChars :: String -> [Int]
findAdjacentChars [] = []
findAdjacentChars [_] = []
findAdjacentChars xs = [i | (i, (a, b)) <- zip [1 ..] (zip xs (tail xs)), a == b]

結果を見ると、今回の実行時間は2213 msでした。

で、計算量を抑えるためにどうすればいいかというと、scanl(https://hoogle.haskell.org/?hoogle=scanl)で計算途中の値をリストに残して置けるのでこれを使います。書き直したコードはこんな感じです。qsの各要素に対してlengthをしていたのがなくなったので、計算量もO(n)になっているはずです。

main :: IO ()
main = do
  [_, q] <- readInputIntList
  ss <- V.fromList <$> getLine
  qs <- V.fromList <$> replicateM q readPairInt

  let counts = V.scanl' (\acc (a, b) -> if a == b then acc + 1 else acc) 0 $ V.zip ss (V.tail ss)

  mapM_ (print . \(l, r) -> counts V.! (r - 1) - counts V.! (l - 1)) qs

これをすると、以下のコードで重複した個数を算出することができます。

counts V.! (r - 1) - counts V.! (l - 1)

実行時間が343msまで短縮できました。

全体を振り返って

解けない問題ではなかったのですが、scanlを使う発想が浮かばずにTLEになってしまいました。前にもこう言うことあったのにな。。
場数が足りていない気がしているので、もっと過去のdailyコンテストとか使って解く問題数を増やしていこうと思います。
別件ですが、今回からcopilotを切って参加してみました。copilotをつけているとどうしても頼ってしまって解法が身に付きづらいのと、思考がcopilotに分断される感覚があったためです。
copilotが無くなったことで定型的な処理を書くのは面倒になりますが、こちらの方が自分が競プロやるにはいい気がします。もっと自分の頭を使ったほうが速い気がする。
もっと強いプログラマになりたいなーー


この記事が気に入ったらサポートをしてみませんか?