24
하하하하 하하하하 하하 하하 2 Codeforces Round 364 (Div 2)

하스켈로 알고리즘 문제 풀기 2

  • Upload
    -

  • View
    182

  • Download
    3

Embed Size (px)

Citation preview

Page 1: 하스켈로 알고리즘 문제 풀기 2

하스켈로 알고리즘 문제 풀기 2Codeforces Round 364 (Div 2)

Page 2: 하스켈로 알고리즘 문제 풀기 2

Codeforces Round 364 (Div 2)• http://codeforces.com/contest/701

• 처음 다섯 문제 분석• A. Cards

• B. Cells Not Under Attack

• C. They Are Everywhere

• D. As Fast As Possible

• E. Connecting Universities

• 문제의 정확한 서술과 입출력 예시는 원문 참조

Page 3: 하스켈로 알고리즘 문제 풀기 2

A. Cards• http://codeforces.com/contest/701/problem/A

• 키워드 : 리스트 모나드• 문제 : 숫자가 적힌 카드 n 개를 n/2 명에게 2 개씩 배분한다 .

각 플레이어가 가진 패의 합이 같도록 하라 .• 카드 개• 카드에 적힌 수 • 답이 있음이 보장된다• 답이 여러 개일 경우 아무거나 출력

Page 4: 하스켈로 알고리즘 문제 풀기 2

A. Cards• 입력 : n 이 작기 때문에 그냥 String 과 리스트 사용

main = do n <- read `fmap` getLine :: IO Int ns <- (map read . words) `fmap` getLine :: IO [Int]

• 한 플레이어의 두 카드의 합 * 플레이어 수 = 모든 카드의 합 let goal = sum ns `div` (n `div` 2)

• 모든 경우의 수 생성 , 합이 같은 경우만 필터링 , 처음 해 출력 let picks = pick2 ns n goal let soln = head (filter same picks) printSoln soln

• 리스트 모나드 이용• 정석 풀이는 카드를 정렬해서 양 끝부터 짝짓는 것

• http://codeforces.com/blog/entry/46283

Page 5: 하스켈로 알고리즘 문제 풀기 2

import Data.Listimport Control.Monad

getInt = read `fmap` getLine :: IO Int

main = do n <- getInt ns <- (map read . words) `fmap` getLine :: IO [Int] let goal = sum ns `div` (n `div` 2) let picks = pick2 ns n goal let soln = head (filter same picks) printSoln soln

pick2 ns n goal = f (zip [1..n] ns) n where f ns 0 = return [] f ns n = do (i,x) <- ns let ns' = delete (i,x) ns (j,y) <- ns' let ns'' = delete (j,y) ns' guard $ (x+y) == goal rest <- f ns'' (n-2) return $ (i,x):(j,y):rest

same ((i,x):(j,y):ns) = f (x+y) ns where f z [] = True f z ((i,x):(j,y):ns) | z == x + y = f z ns | otherwise = False

printSoln [] = return ()printSoln ((i,x):(j,y):ns) = do putStrLn $ show i ++ " " ++ show j printSoln ns

A. Cards

Page 6: 하스켈로 알고리즘 문제 풀기 2

B. Cells Not Under Attack• http://codeforces.com/contest/701/problem/B

• 키워드 : IntSet, Int64

• 문제 : n x n 체스판에 룩 m 개를 겹치지 않게 차례대로 놓는다 . 룩을 놓을 때마다 어떤 룩에게서도 안전한 칸의 개수를 출력하라 .• 체스판 크기• 룩 개수

Page 7: 하스켈로 알고리즘 문제 풀기 2

B. Cells Not Under Attack• 룩을 (x, y) 에 놓으면 x 열 또는 y 행의 모든 칸은 안전하지 않다• 룩이 하나라도 놓인 열이 xs 개 , 행이 ys 개라 하면

• 안전한 칸은 (n-xs) * (n-ys) 개• 체스판의 크기는 최대 이므로 64 비트 정수 필요

• 내 컴퓨터에서는 Int 가 64 비트였으나 채점기에서는 32 비트• 콘테스트 당시 왜 오답인지 몰라서 통과 실패

• 이미 룩이 있는 열 또는 행을 저장할 자료구조• 리스트를 쓰면 시간 초과 ( 쓰기는 O(1), 검색은 O(N)) -> 최종 시간복잡도 O()

• 배열 (Data.Array) 은 못 쓴다 ( 쓰기가 O(N). 그리고 MArray 는 코드포스에서 막아놓음 )

• IntSet 활용• Set 과 Map 은 쓰기 가능한 배열이 필요한 알고리즘을 하스켈로 구현할 때 적절한 대안

Page 8: 하스켈로 알고리즘 문제 풀기 2

B. Cells Not Under Attackimport Data.List hiding (insert)import Data.Intimport Data.IntSet hiding (map)

main = do [n,m] <- (map read . words) `fmap` getLine :: IO [Int] rocks <- (map (map read . words) . take m . lines) `fmap` getContents :: IO [[Int]] let n64 = fromIntegral n :: Int64 solve rocks empty empty n64 n64

solve [] cols rows emptyCols emptyRows = return ()solve ([x,y]:rocks) cols rows emptyCols emptyRows = do let col_already = member x cols let row_already = member y rows let emptyCols' = if col_already then emptyCols else emptyCols - 1 let emptyRows' = if row_already then emptyRows else emptyRows - 1 putStr $ show $ emptyCols' * emptyRows' putChar ' ' let cols' = insert x cols let rows' = insert y rows solve rocks cols' rows' emptyCols' emptyRows'

Page 9: 하스켈로 알고리즘 문제 풀기 2

C. They Are Everywhere• http://codeforces.com/contest/701/problem/C

• 키워드 : Data.Map, 그리디 알고리즘• 문제 : 방이 일렬로 n 개 있고 각각 바로 왼쪽 , 오른쪽 방과 연결된 집이 있다 .

각 방에는 포켓몬이 하나 있다 . ( 포켓몬은 글자 a~z, A~Z 중 하나로 표현 ) 한 방에서 출발해 모든 종류를 잡을 때까지 오른쪽 방으로 건너간다 . 방문해야 하는 방의 최소 개수를 구하여라 .• 방의 개수 • 해가 있음이 보장된다

Page 10: 하스켈로 알고리즘 문제 풀기 2

C. They Are Everywhere• 1 번 방에서 시작해 모든 종류를 잡을 때까지 전진

• 현재 해 : (1, )

• 지나온 방의 개수 계산• 각 방을 방문하며 잡은 포켓몬 기록

• 1 번 방에서 잡은 포켓몬을 기록에서 제거• 번 방에서 시작해 모든 종류를 잡을 때까지 전진• 지나온 방의 개수 계산• 최적해 = min (, ) 갱신

• 2 번 방에서 잡은 포켓몬을 기록에서 제거• 방에서 시작해 모든 종류를 잡을 때까지 전진• …

• m 번 방에서 잡은 포켓몬을 기록에서 제거• 더 이상 모든 종류를 잡을 수 없으면 종료

Page 11: 하스켈로 알고리즘 문제 풀기 2

C. They Are Everywhere• Data.Set 은 어떤 값이 집합에 소속되는지만 검사 가능• Data.Map 을 이용해서 키 ( 포켓몬 종류 ) 를 값 ( 그 종류를 잡은 개수 ) 으로 매핑

Page 12: 하스켈로 알고리즘 문제 풀기 2

import Data.Listimport Data.Arrayimport Data.Maybeimport qualified Data.Map.Strict as M

main = do n <- read `fmap` getLine :: IO Int flats <- getLine let flatAry = listArray (1, length flats) flats let numPoke = (length . group . sort) flats print $ solve numPoke flatAry

type Book = M.Map Char Inttype Solution = (Book, Int, Int) -- (currSet, beginIx, endIx)

solve numPoke flats = f (M.empty, 1, 1) 100000 where f :: Solution -> Int -> Int f curr best = case walk numPoke flats curr of Nothing -> best Just next -> f (removeHead next flats) (min best (cost next)) cost (_, b, e) = e - b

removeHead (book, b, e) flats = (book', b+1, e) where x = flats ! b book' = if book M.! x == 1 then M.delete x book else M.adjust (\y->y-1) x book

walk :: Int -> Array Int Char -> Solution -> Maybe Solutionwalk num flats (book, b, e) = f book e where (ary1, aryN) = bounds flats len = aryN - ary1 + 1 f book e | e > len && M.size book >= num = Just (book, b, e) | e > len && M.size book < num = Nothing | M.size book == num = Just (book, b, e) | otherwise = f (M.insertWith (+) (flats ! e) 1 book) (e + 1)

C. They Are Everywhere

Page 13: 하스켈로 알고리즘 문제 풀기 2

D. As Fast As Possible• http://codeforces.com/contest/701/problem/D

• 키워드 : ad hoc, 이진 탐색 (binary search)

• 문제 : 일직선 도로의 한쪽 끝에 사람 n 명 , k 명을 태울 수 있는 버스가 있다 . 각자 버스를 최대 한 번만 탈 수 있다 . 반대편 끝으로 도달하는 최소 시간을 구하여라 .• 버스 탑승 , 하차 , 버스가 방향을 돌리는 것은 시간이 걸리지 않는다 .

• 사람 수 • 버스의 수용량 • 사람과 버스의 속도 • 도로 길이

Page 14: 하스켈로 알고리즘 문제 풀기 2

D. As Fast As Possible• 이진 탐색을 이용한 해법 : http://pakapa104.hatenablog.com/entry/2016/07/25/135255

• 하지만 식을 계산해서 O(1) 해답 도출 가능• 사람들을 개 그룹으로 분할한다 .

• 모든 그룹은 버스를 타는 시간이 x 로 동일하다 .• 걷는 것보다 버스를 타는 것이 무조건 빠르다• 버스를 한 그룹이 x + e, 다른 그룹이 x – e 시간 탄다고 가정 (e > 0)

• x - e 시간 타는 그룹은 e 시간만큼 더 걷게 된다 -> 더 오래 걸린다• 따라서 모든 그룹은 버스를 타는 시간이 같아야 한다

Page 15: 하스켈로 알고리즘 문제 풀기 2

D. As Fast As Possible• 버스가 한 그룹을 태우고 갔다가 나머지 그룹에게 돌아오는 시간 y

• 버스 전진 :

• 보행자 전진 :

• 거리 차이 : • 버스 후진 , 보행자 전진 :

• 버스를 타는 시간 x 의 최대값은 ?

• 그룹 • 은 버스를 x 시간 타고 남은 거리를 걸어간다• x + y 시간을 걷고 버스를 x 시간 타고 남은 거리를 걸어간다• 2(x + y) 시간을 걷고 버스를 x 시간 타고 남은 거리를 걸어간다• …

• 이간을 걷고 남은 거리는 버스를 타기에 충분히 길어야 한다 ( )

Page 16: 하스켈로 알고리즘 문제 풀기 2

D. As Fast As Possible• 은 간을 걷고 남은 거리는 버스를 탄다• 이 걸은 거리 :

• x 의 최대값은 • 은 먼저 도착• 의 총 이동 시간 이 답

Page 17: 하스켈로 알고리즘 문제 풀기 2

D. As Fast As Possiblemain = do ps@[n, l, v1, v2, k] <- (map read . words) `fmap` getLine :: IO [Int] let ps' = map fromIntegral ps :: [Double] print $ solve ps'

solve :: [Double] -> Doublesolve [n, l, v1, v2, k] = t1 + x where m = fromIntegral (ceiling (n / k)) :: Double v3 = (v2 - v1) / (v2 + v1) x = l / (v2 + (m-1)*(v1 * (1+ v3))) t1 = (l - v2*x) / v1

Page 18: 하스켈로 알고리즘 문제 풀기 2

E. Connecting Universities• http://codeforces.com/contest/701/problem/E

• 키워드 : 그래프 , 스패닝 트리 , 깊이 우선 탐색 (DFS), ByteString

• 문제 : 마을 n 개가 간선 n-1 개로 연결되어 스패닝 트리를 형성한다 . 이 중 2k 개 마을에 도시가 있고 , 모든 간선의 가중치는 1 이다 . 도시들을 k 개 짝으로 묶으려 하는데 , 모든 짝의 거리의 합이 최대가 되도록 한다 . 거리의 합의 최댓값을 구하여라 .• 마을 수 • 도시 수

• 참고 자료• http://kenkoooo.hatenablog.com/entry/2016/07/23/043406

• http://blog.csdn.net/baidu_19306071/article/details/52005557

Page 19: 하스켈로 알고리즘 문제 풀기 2

E. Connecting Universities• 두 마을 u, v 를 연결하는 임의의 간선에 대해

• u 쪽에 있는 도시가 x 개 , v 쪽에 있는 도시가 2k - x 개라 하면• u 쪽의 도시와 v 쪽의 도시를 짝지어야 한다

• 가능한 짝은 min(x, 2k - x) 개• 한 쪽의 두 도시를 짝지으면 무조건 거리의 총합이 낮아진다

vu

Page 20: 하스켈로 알고리즘 문제 풀기 2

E. Connecting Universities

• u 쪽의 도시와 v 쪽의 도시끼리 짝지을 경우 비용 P• 다음 두 비용의 합• path(u1, u) + path(u, v) + path(v, v1)

• path(u2, u) + path(u, v) + path(v, v2)

• u 쪽의 도시끼리 , v 쪽의 도시끼리 짝지을 경우 비용 Q

• Q = path(u1, u2) + path(v1, v2)

• Q <= path(u1,u) + path(u,u2) + path(v1, v) + path(v, v2) < P

vu

u1

u2

v1

v2

Page 21: 하스켈로 알고리즘 문제 풀기 2

E. Connecting Universities• 한 마을을 루트 삼아 순회 시작

• 마을마다 해당 마을을 포함해 DFS 트리상 그 아래에 있는 도시의 개수 x 계산• 마을에 도시가 있으면 x = 1, 없으면 x = 0 으로 설정하고 자식 노드들의 x 를 더한다

• 후위 순회 (postorder traversal) 필요• 모든 마을의 min(x, 2k-x) 를 더한 것이 답

• 한 간선에 저장된 값이 min(x, 2k-x) 인 것의 의미• 이 간선을 지나는 경로가 min(x, 2k-x) 개• 간선의 가중치 ( 길이 ) 는 1

• 모든 간선에 저장된 값을 더하면 모든 경로의 길이를 더한 것이 된다

Page 22: 하스켈로 알고리즘 문제 풀기 2

E. Connecting Universities• 입력이 너무 길어서 ByteString 사용 (String 쓰면 시간 초과 )

• -- getInts = (map read . words) `fmap` getLine :: IO [Int]• getInts = (map (fst . fromJust . BS8.readInt) . BS8.words) `fmap` BS.getLine :: IO

[Int]

• 그래프는 인접 리스트로 표현• type Tree = Map.Map Int (Int, [Int]) -- (data, children)

• 답이 Int 의 범위를 넘어서 Integer 사용• answer :: Tree -> Int -> Int -> Integer

• Integer 는 하스켈에 내장된 big integer 타입• 답이 얼마나 큰지 따지기 귀찮을 때 사용 . 계산 속도는 Int 보다 느리다

Page 23: 하스켈로 알고리즘 문제 풀기 2

import Control.Monadimport Data.Arrayimport Data.Maybeimport qualified Data.Map.Strict as Mapimport qualified Data.ByteString as BSimport qualified Data.ByteString.Char8 as BS8

getInt = read `fmap` getLine :: IO Int--getInts = (map read . words) `fmap` getLine :: IO [Int]getInts = (map (fst . fromJust . BS8.readInt) . BS8.words) `fmap` BS.getLine :: IO [Int]

type Tree = Map.Map Int (Int, [Int]) -- (data, children)

main = do [n, k] <- getInts cities <- getInts edges <- replicateM (n-1) getInts let tree0 = Map.fromList [(key, (0,[])) | key <- [1..n]] :: Tree let tree = foldl addCity (foldl addEdge tree0 edges) cities let t = dfs tree 1 print $ answer t n (k*2)

addEdge :: Tree -> [Int] -> TreeaddEdge g [u,v] = g'' where g' = Map.update (\(x,es) -> Just (x,u:es)) v g g'' = Map.update (\(x,es) -> Just (x,v:es)) u g'

addCity :: Tree -> Int -> TreeaddCity g i = Map.update (\(_,es) -> Just (1,es)) i g

dfs :: Tree -> Int -> Treedfs tree start = f 0 tree start where link curr prev = filter (/= prev) $ snd (tree Map.! curr) f prev tree curr | snd (tree Map.! curr) == [] = tree | otherwise = let x = foldl (f curr) tree (link curr prev) in seq x (calc x curr prev) calc tree curr prev = Map.update (\(d,es) -> Just (d+cost,es)) curr tree where cost = sum $ map (\j -> fst (tree Map.! j)) (link curr prev) :: Int

answer :: Tree -> Int -> Int -> Integeranswer tree n k2 = sum $ map (\i -> min (cnt i) (k2' - (cnt i))) [1..n] where k2' = fromIntegral k2 cnt i = fromIntegral $ fst (tree Map.! i)

E. Connecting Universities

Page 24: 하스켈로 알고리즘 문제 풀기 2

E. Connecting Universities• 시간 제한 : 3 초• 메모리 제한 : 256MB

• 간신히 통과