summaryrefslogtreecommitdiff
path: root/2017/7.hs
blob: 258990e2ea75107b2ec0778e56ccd1a9901257d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import Control.Monad
import qualified Data.Map.Strict as Map
import qualified Data.Map.Lazy as LMap
import Data.Map.Strict ((!))
import Data.Maybe
import Data.List
import qualified Data.Set as Set


data Program = Program {progName :: String, progWeight :: Int, progChildren :: [String]}
  deriving Show

parse :: String -> Program
parse s = go (words s)
  where
    go [name, weight] = Program name (readweight weight) []
    go (name : weight : "->" : ch) = Program name (readweight weight) (map (filter (/= ',')) ch)
    readweight = read . init . tail

findParents :: [Program] -> (Map.Map String String, Set.Set String)
findParents allprogs = go allprogs Map.empty (Set.fromList (map progName allprogs))
  where
    go [] parmap nopar = (parmap, nopar)
    go (Program name _ ch : progs) parmap nopar =
        let parmap' = foldl (\m c -> Map.insert c name m) parmap ch
            nopar' = foldl (\s c -> Set.delete c s) nopar ch
        in go progs parmap' nopar'

towerWeights :: [Program] -> Map.Map String Int
towerWeights progs = resmap
  where
    resmap = LMap.fromList [(progName p, go p) | p <- progs]
    go prog@(Program _ w ch) = w + sum [resmap ! c | c <- ch]

alleq :: Eq a => [a] -> Bool
alleq (a:b:cs) = a == b && alleq (b:cs)
alleq _ = True

oneout :: Eq a => [a] -> (Int {-idx-}, a {-mode-})
oneout l = go l 0
  where
    go (a:b:c:_) 0 | a /= b && b == c = (0, b)
    go [a,b,c] i | a == b && b /= c = (i + 2, a)
    go (a:b:c:_) i | a /= b && b /= c = (succ i, a)
    go (_:xs) i = go xs (succ i)

main :: IO ()
main = do
    input <- liftM lines (readFile "7.in")
    let progs = map parse input
        (parmap, nopar) = findParents progs
        [root] = Set.toList nopar

    putStrLn root

    let progmap = Map.fromList [(progName p, p) | p <- progs]
        tw = towerWeights progs
        unbalnames = map progName $ filter (\(Program _ _ ch) -> not $ alleq $ map (tw !) ch) progs
        [unbalname] = unbalnames \\ catMaybes (map (flip Map.lookup parmap) unbalnames)
        unbal = progmap ! unbalname
        (wrongidx, otherw) = oneout (map (tw !) (progChildren unbal))
        wrongprog = progmap ! (progChildren unbal !! wrongidx)

    print $ progWeight wrongprog + otherw - (tw ! progName wrongprog)