トップ «前の日記(2010-04-29) 最新 次の日記(2010-05-14)» 月表示 編集

日々の流転


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 今探したら残念ながら見つからなかったけど