-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathcsa.hs
130 lines (103 loc) · 4.18 KB
/
csa.hs
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import System.IO
import qualified Data.Map as M
import Data.Maybe (fromMaybe, fromJust)
import Control.Applicative
import Control.Monad (unless)
import Numeric
import qualified Data.ByteString.Char8 as BS (ByteString, getLine, readInt, words, null)
readInts :: BS.ByteString -> [Int]
readInts = map (fst . fromJust . BS.readInt) . BS.words
type Station = Int
type Timestamp = Int
infinity :: Timestamp
infinity = maxBound
-- Evaluate Maybe Timestamp with infinity as fallback
timestamp :: Maybe Timestamp -> Timestamp
timestamp = fromMaybe infinity
-- Connection
-- departureStation arrivalStation departureTime arrivalTime
data Connection = Connection Station Station Timestamp Timestamp
newConnection :: [Int] -> Connection
newConnection [departure, arrival, departureTime, arrivalTime] =
Connection departure arrival departureTime arrivalTime
newConnection _ = error "Illegal Connection values"
parseConnection :: BS.ByteString -> Connection
parseConnection = newConnection.readInts
printConnection :: Connection -> String
printConnection (Connection departure arrival departureTime arrivalTime) =
unwords . map show $ [departure, arrival, departureTime, arrivalTime]
-- Query
-- departureStation arrivalStation departureTime
data Query = Query Station Station Timestamp
newQuery :: [Int] -> Query
newQuery [departure, arrival, departureTime] = Query departure arrival departureTime
newQuery _ = error "Illegal Query values"
parseQuery :: BS.ByteString -> Query
parseQuery = newQuery.readInts
-- Timetable
-- arrivalTimes inConnections
data Timetable = Timetable IndexedTimestamps IndexedConnections
type IndexedTimestamps = M.Map Station Timestamp
type IndexedConnections = M.Map Station Connection
emptyTimetable :: Query -> Timetable
emptyTimetable (Query departure _ departureTime) =
Timetable (M.insert departure departureTime M.empty) M.empty
buildTimetable :: Query -> [Connection] -> Timetable
buildTimetable = (augmentTimetable infinity) .emptyTimetable
augmentTimetable :: Timestamp -> Timetable -> [Connection] -> Timetable
augmentTimetable _ timetable [] = timetable
augmentTimetable earliestArrival timetable@(Timetable arrivalTimes inConnections) (connection : connections) =
let Connection departure arrival departureTime arrivalTime = connection
bestDepartureTime = timestamp $ M.lookup departure arrivalTimes
bestArrivalTime = timestamp $ M.lookup arrival arrivalTimes
in
if bestDepartureTime <= departureTime && bestArrivalTime > arrivalTime
then
let newArrivalTimes = M.insert arrival arrivalTime arrivalTimes
newInConnections = M.insert arrival connection inConnections
newTimetable = Timetable newArrivalTimes newInConnections
in augmentTimetable (min arrivalTime earliestArrival) newTimetable connections
else
if arrivalTime > earliestArrival
then
timetable
else
augmentTimetable earliestArrival timetable connections
-- CSA implementation
findPath :: Timetable -> Query -> Path
findPath (Timetable _ inConnections) (Query _ arrival _) = findPathImpl inConnections arrival []
type Path = [Connection]
findPathImpl :: IndexedConnections -> Station -> [Connection] -> [Connection]
findPathImpl inConnections objective accumulator =
case M.lookup objective inConnections of
Nothing -> accumulator
Just connection ->
let Connection departure _ _ _ = connection
in findPathImpl inConnections departure (connection : accumulator)
readConnections :: IO [BS.ByteString]
readConnections = do
line <- BS.getLine
if BS.null line
then return []
else (line :) <$> readConnections
printPath :: Path -> IO ()
printPath [] = putStrLn "NO_SOLUTION"
printPath path = mapM_ (putStrLn . printConnection) path
mainLoop :: [Connection] -> IO ()
mainLoop connections = do
done <- isEOF
unless done $ do
line <- BS.getLine
unless (BS.null line) $ do
let query = parseQuery line
let timetable = buildTimetable query connections
printPath $ findPath timetable query
putStrLn ""
hFlush stdout
mainLoop connections
-- main
main :: IO ()
main = do
firstLines <- readConnections
let connections = fmap parseConnection firstLines
mainLoop connections