2010-05-08 [長年日記]
λ. Google Code Jam 2010 Qualification Round
問題はそんなに難しい問題ではなかったのだけど、思いの他苦戦してしまった。 次のラウンドには一応進めたけれど、感覚が鈍ってるなぁ……
A. Snapper Chain
3問のうち一番苦戦したのがこの問題。 最初に書いたのは以下のようなプログラムで、Smallは簡単に解けるのだけど、Largeの上限のデータとかを生成して食わせると、とても終了しない。
{-# LANGUAGE Rank2Types, ScopedTypeVariables #-}
import Data.Array.ST
import Control.Monad
import Control.Monad.ST
import Text.Printf
f :: forall st. Int -> Int -> ST st Bool
f n k = do
(power :: STUArray st Int Bool) <- newArray (1,n+1) False
(state :: STUArray st Int Bool) <- newArray (1,n) False
let updatePower = do
writeArray power 1 True
forM_ [2..n+1] $ \pos -> do
b1 <- readArray power (pos - 1)
b2 <- readArray state (pos - 1)
writeArray power pos (b1 && b2)
updateState = do
forM_ [1..n] $ \pos -> do
isPowered <- readArray power pos
when isPowered $ do
isON <- readArray state pos
writeArray state pos (not isON)
updatePower
forM_ [1..k] $ \_ -> updateState >> updatePower
readArray power (n+1)
main :: IO ()
main = do
t <- readLn
forM_ [(1::Int)..t] $ \i -> do
[n,k] <- liftM (map read . words) $ getLine
printf "Case #%d: %s\n" i (if runST (f n k) then "ON" else "OFF")
Largeサイズの入力を扱えるようにと数回プログラムを書き直したが、その途中でトレースを表示して、初めて二進数のインクリメントになっている事に気づいた。 問題文の文章にちょっと騙された。
import Control.Monad
import Text.Printf
import Data.Bits
f :: Int -> Int -> Bool
f n k = and [testBit k i | i<-[0..n-1]]
main :: IO ()
main = do
t <- readLn
forM_ [(1::Int)..t] $ \i -> do
[n,k] <- liftM (map read . words) $ getLine
printf "Case #%d: %s\n" i (if f n k then "ON" else "OFF")
B. Fair Warning
Bは個人的には3問の中で一番簡単で、特に工夫は必要なかった。
import Control.Monad
import Text.Printf
f :: [Integer] -> Integer
f ts = (t - ((ts !! 0) `mod` t)) `mod` t
where t = g ts
g :: [Integer] -> Integer
g ts = foldl1 gcd [ti - tj | ti <- ts, tj <- ts, ti > tj]
main :: IO ()
main = do
c <- readLn
forM_ [(1::Int)..c] $ \i -> do
(n:ts) <- liftM (map read . words) $ getLine
printf "Case #%d: %d\n" i (f ts)
【2010-05-27追記】 Google Code Jam 2010 Qualification Round — Scratch Leaf — MAYAH.JP によれば、上のソースのgは以下のようにすれば十分で、より少ない計算量で出来たらしい。そりゃそうだ。
g :: [Integer] -> Integer g ts = foldl1 gcd [ti - t0 | ti <- ts', ti /= t0] where (t0:ts') = sort ts
C. Theme Park
最初に書いたのはC.hs。 Smallは当然解けるのだけど、甘い見積もりでLargeをダウンロードしてしまったら、時間内に解けなくて撃墜。 ちょっと工夫して、テーブル化して高速なべき乗法を使うようにしたら解けた(C2.hs)。 Largeはちゃんと性能的にも解けると確信してからダウンロードしないと駄目だなぁ。勿体無いことをした。 これは実際に参加してみて初めて感じた点。
【追記】 誰かがこの問題についてTwitterで「鳩ノ巣原理の良い例題」と書いていて*1、そのときは意味が分からなかったけど、後でku-ma-meさんに説明していて、そういう方法もあったと気づいた。
*1 今探したら残念ながら見つからなかったけど
