Problem 212
コンテンツ
212 Combined Volume of Cuboids
はじめは平面走査で行こうと思ったが…
遅い。おそらく、直方体が密に存在するから。
そこで、方針を変更。
分割統治で。大きな直方体はないから、大丈夫でしょう。
import Data.List ((!!), mapAccumL) import Control.Arrow ((***)) type Cuboid = ([Int], [Int]) lagFib :: [Integer] lagFib = map s [1..55] ++ zipWith add lagFib (drop 31 lagFib) where s k = (100003 - 200003*k + 300007*k^3) `mod` 1000000 add x y = (x + y) `mod` 1000000 cuboids :: [Cuboid] cuboids = map h.getCube $ lagFib where getCube (x:y:z:dx:dy:dz:fibs) = (map f [x,y,z], map g [dx,dy,dz]) : getCube fibs f x = fromIntegral $ x `mod` 10000 g x = fromIntegral $ 1 + (x `mod` 399) h (p,dp) = (p, zipWith (+) p dp) replace :: Int -> a -> [a] -> [a] replace n x xs = let (a,b:cs) = splitAt n xs in a ++ x:cs split :: Int -> Int -> Cuboid -> ([Cuboid], [Cuboid]) split n t c@(p1, p2) | t <= p1!!n = ([], ) | p2!!n <= t = (, []) | otherwise = ([c1], [c2]) where c1 = (p1, replace n t p2) c2 = (replace n t p1, p2) partition :: [Cuboid] -> (Int, Int) -> ([Cuboid], [Cuboid]) partition cs (n,t) = (concat***concat).unzip.map (split n t) $ cs volume :: Cuboid -> Integer volume (p1,p2) = product.map toInteger.zipWith (-) p2 $ p1 combineVolume :: [Cuboid] -> Integer combineVolume [] = combineVolume (c@(p1,p2):cs) = volume c + sum (map combineVolume $ outer ++ inner) where (rs,outer) = mapAccumL partition cs $ zip [..2] p2 (_,inner) = mapAccumL partition' rs $ zip [..2] p1 partition' xs = ((a,b) -> (b,a)).partition xs main :: IO () main = print.combineVolume.take 50000 $ cuboids
ついでに、ダメだった平面走査。
{-# LANGUAGE BangPatterns #-} import Data.List (sort, foldl') import Data.Set (empty, toList, insert, delete) lagFib :: [Integer] lagFib = map s [1..55] ++ zipWith add lagFib (drop 31 lagFib) where s k = (100003 - 200003*k + 300007*k^3) `mod` 1000000 add x y = (x + y) `mod` 1000000 cuboids = getCube lagFib where getCube (x:y:z:dx:dy:dz:fibs) = ((f x,f y,f z),(g dx,g dy,g dz)):getCube fibs f x = fromIntegral $ x `mod` 10000 g x = fromIntegral $ 1 + (x `mod` 399) data Event = L | U deriving (Eq, Ord, Show) type Coord = Int type Line = (Coord, Coord) type Box = ((Coord, Coord), (Coord,Coord)) type Cube = ((Coord, Coord, Coord), (Coord, Coord, Coord)) combineLine :: [Line] -> Integer combineLine = fst.foldl' step (,).sort step (!s,!p) (!x,!dx) | x+dx < p = (s, p) | p < x = (s+ toInteger dx, x+dx) | otherwise = (s+ toInteger (x+dx-p), x+dx) boxToEvent :: Box -> [(Coord, Line, Event)] boxToEvent ((x,y), (dx,dy)) = [(y,l,L),(y+dy,l,U)] where l = (x,dx) combineBox :: [Box] -> Integer combineBox = snd.foldl' sweep ((,empty,), ).sort.concatMap boxToEvent where sweep ((!h,!ls,!w), !a) (!y,!l,!e) = let a' = a + w*(toInteger (y-h)) ls' = case e of L -> insert l ls U -> delete l ls w' = combineLine.toList $ ls' in ((y,ls',w'), a') cubeToEvent :: Cube -> [(Coord, Box, Event)] cubeToEvent ((x,y,z), (dx,dy,dz)) = [(z,b,L),(z+dz,b,U)] where b = ((x,y), (dx,dy)) combineCube :: [Cube] -> Integer combineCube = snd.foldl' sweep ((,empty,), ).sort.concatMap cubeToEvent where sweep ((!h,!bs,!a), !v) (!z,!b,!e) = let v' = v + a*(toInteger(z-h)) bs' = case e of L -> insert b bs U -> delete b bs a' = combineBox.toList $ bs' in ((z,bs',a'), v') main :: IO () main = print.combineCube.take 100 $ cuboids
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)