GCJJ 予選に参加してみた

x86/64 最適化勉強会中に、iPad からリモートで家の PC に接続して、そこでやってました。
GCJJ に集中しすぎて勉強会の方を全然聞けなかったっていう・・・ほんとすみません。


ということで A と C が解けました。
B は満足度優先で探してたのですが、それだけだとダメだったようで・・・で、家に帰ってからいくつか修正したのですがそれでも通らなかったので根本的に考え方が間違ってるようです。で、一位の人のコード読んだら何でこれで解けるんだろうという謎のコードでした。


A のコードです。

import System (getArgs)

type Value = Int
type Index = Int
type Size = Int
type Chunk = (Index,Size)

getValue :: Int -> [Chunk] -> Index -> Value
getValue m cs ix = foldr (get m) ix cs
  where
    get :: Int -> Chunk -> Index -> Index
    get m (i,s) ix | 0     <= ix && ix < s     = ix+i
                   | s     <= ix && ix < (s+i) = ix-s
                   | (i+s) <= ix && ix < m     = ix

{-
0 <= W < M
0 <= A < M
1 <= B <= M
-}
getInput :: FilePath -> IO [(Int,[Chunk],Index)]
getInput file = readFile file >>= return.parse.lines
  where
    parse :: [String] -> [(Int,[Chunk],Index)]
    parse (num:ls) = take (read num) (parse2 ls)
    parse2 :: [String] -> [(Int,[Chunk],Index)]
    parse2 (l:ls) = (m,chunks,w-1):parse2 ls'
      where (m:c:w:_) = map read (words l)
            (chunks,ls') = parse3 c ls
    parse3 :: Int -> [String] -> ([Chunk],[String])
    parse3 num ls = (take num (map toChunk ls),drop num ls)
      where toChunk str = (i-1,s) where (i:s:_) = map read (words str)

output :: Int -> Value -> IO ()
output n v = putStrLn $ "Case #"++show n++": "++show (v+1)

main = do
  inputFile <- getArgs >>= return . head
  results <- getInput inputFile >>= return . map (\(a,b,c) -> getValue a b c)
  mapM_ (uncurry output) $ zip [1..] results

見つけたいインデックスの位置だけを監視しておいて、そこから逆順に戻していけば初期状態での位置が分かるから解けるということはすぐ分かったのですが、なぜか逆順ではなく正順でやってしまって誤答2回。素晴らしいことにサンプル入力のテストは通りました。これを見越してわざとそういう入力にしたのかと思うとムキー!という感じですね(実際はそうじゃないみたいですけど)。
でもって A 番目が 0 オリジンだと思ってて間違ったという素晴らしいこともやってしまって、ついカッとなって入力を 0 オリジンに書き換えてしまいました。
あと Haskell で入力をパースするのめちゃめちゃめんどいです。もっと Haskell にやさしい入力をお願いします。


C のコード。

import System (getArgs)

nearPower2 0 = 0
nearPower2 x = 2^(near 0 x)-1
  where
    near a x | 2^a-1 > x = a-1
             | otherwise = near (a+1) x

countBits 0 = 0
countBits x =
  let (r,q) = x `divMod` 2
  in q + countBits r

solve :: Integer -> Integer
solve x =
  let a = nearPower2 x
      b = x-a
  in countBits a + countBits b

getInput :: FilePath -> IO [Integer]
getInput file = readFile file >>= return.parse.lines
  where
    parse :: [String] -> [Integer]
    parse (num:ls) = take (read num) (parse2 ls)
    parse2 :: [String] -> [Integer]
    parse2 ls = map read ls

output :: Int -> Integer -> IO ()
output n v = putStrLn $ "Case #"++show n++": "++show v

main = do
  inputFile <- getArgs >>= return . head
  results <- getInput inputFile >>= return . map solve
  mapM_ (uncurry output) $ zip [1..] results

N に一番近い 2^n-1 の値を a として、N-a を b として、それぞれのビット数を数えたら何となく正解しました。
数学的なこととか全く考えず、単なる直感だったのですが意外にサクッと解けました。