Life Goes On

まあまあだけど楽しんでる方です

Google Code Jam 2010 - Qualification Round

今年も参加してます。予選だしあまり言うこともないのですが、せっかくなのでコードを晒しときます。
去年のコードと比べると、ゴルフの影響を受けすぎ(悪い意味で)。もうちょっと可読性を大事にする人間だったはずなのですが・・・。
ともあれ Haskell で解けるというのはウレシイ限りです。

A

最初、問題の意味がぜんぜん分かりませんでした(snapper って何だよ!)。分かれば簡単。2 進表記で指定された桁以下が全部 1 かどうか答える。

main=interact$unlines.zipWith(++)["Case #"++shows i": "|i<-[1..]].map(f.map read.words).tail.lines
f[n,k]=["OFF","ON"]!!(0^mod(k+1)(2^n))

B

与えられた数列の周期を求めて、0 以上の最小値を答える。これもまぁ簡単。

main=interact$unlines.zipWith(++)["Case #"++shows i": "|i<-[1..]].map(f.map read.words).tail.lines
f(_:x:ys)=show$mod(-x)$foldl1 gcd[abs$x-y|y<-ys,y/=x]

C

グループ単位でジェットコースターに乗るのを繰り返すとき、何人乗れるか答える。Small はともかく Large で 10^8*1000 ってフツウにやったら終わらないよなぁ、面倒だなぁとか思いながら書いてみたら、やっぱりものすごく面倒でした。なぜこの問題だけ?
人数のリストが途中から循環するので、循環が始まる点を適当に求めて、それ以前/以後で場合分けして求めてます(6 〜 7 行目の a というやつがそれです)。さすがにこれはもっと簡単に書けるのでは。他の人のコードを読みたい。

import Data.List
main = interact $ unlines.zipWith(++)["Case #"++shows i": "|i<-[1..]].f.map(map read.words).tail.lines
f([r,k,n]:g:x) = show a : f x
  where
  -- 求める人数は循環前の部分の人数+循環部の人数(完全に一周する分)+循環部の人数(途中まで辿る分)
  a | r>d0 = sum g0 + sum g1*((r-d0)`div`d1) + sum(genericTake((r-d0)`mod`d1)g1)
    | otherwise = sum $ genericTake r g0
  -- d0, d1 は g0, g1 の長さ
  [d0,d1] = map genericLength[g0,g1]
  -- g0, g1 は人数のリストの、循環前の部分と循環部
  [g0,g1] = map(snd.unzip)[g0'',g1'']
  g1'' = b : takeWhile(/=b)g2''
  (g0'',_:g2'') = span(/=b)g''
  -- b は循環部に含まれる点。ここから循環部が始まるとする。
  b = g''?tail g''
  -- リストの循環部に含まれる点を見つける。ウサギとカメのロジック
  (x:xs)?(y:_:ys)
    | x==y = x
    | otherwise = xs?ys
  -- g'' の要素は一度にコースターに乗れる人数(ID つき)
  g'' = (0!0)g'
  -- 一度にコースターに乗れる人数を求めてリストにする。
  (s!m)((i,x):y)
    | s+x>k||m+1>n = (i,s) : (0!0)((i,x):y)
    | otherwise = (s+x)!(m+1) $ y
  -- グループのリスト g に ID を振る
  g' = cycle $ zip[0..]g
f _ = []