Compare commits

...

2 commits

Author SHA1 Message Date
nihonium
a31509d093
least reachable city 2022-10-06 21:28:51 +03:00
nihonium
74f5dbb620
working dijkstra 2022-10-06 20:45:41 +03:00
4 changed files with 143 additions and 0 deletions

View file

@ -0,0 +1,75 @@
module LeastReachableCity(dijkstra, leastReachableCity) where
import Types
import qualified Data.Map as Map
import qualified Data.Set as Set
-- Well, it's just a magic number, because it's too much hassle to implement type like "Len val | Infinity"
-- TODO: get graph radius and use this number as infinity
infinity :: Len
infinity = 999999 :: Len
leastReachableCity :: Graph -> Len -> (Point, Int)
leastReachableCity graph mileage = foldl (\(p, n) (p', n') ->
if n' < n then (p',n')
else (p, n))
(-1, infinity) [(city, countReachableCities $ dijkstra city graph) | city <- Set.toList $ fst $ countVertices graph] where
countReachableCities :: Distances -> Int
countReachableCities distances = Map.foldl (\n val -> if (val <= mileage) && (val /= 0) then n + 1 else n) 0 distances
dijkstra :: Point -> Graph -> Distances
dijkstra start graph = dijkstra' start graph visited_init to_visit_init distances_init where
-- Helper (so we don' need to call dijkstra initializing empty visited, to_visit etc)
dijkstra' :: Point -> Graph -> Vertices -> Vertices -> Distances -> Distances
dijkstra' start graph visited to_visit distances = if Set.null to_visit
then
distances
else
dijkstra'
(findMinNotVisited to_visit distances)
graph
(Set.insert start visited)
(Set.union (Set.delete start to_visit) $ findNotVisitedNeighbours graph visited start)
(updateDistances graph start distances)
-- Other
to_visit_init = (Set.insert start $ Set.empty :: Vertices)
distances_init = (infinityDistances start $ snd $ countVertices graph)
visited_init = (Set.empty :: Vertices)
updateDistances :: Graph -> Point -> Distances -> Distances
updateDistances graph point distances = snd $ Map.foldrWithKey decideMin ((point, Map.findWithDefault (-1) point distances), Map.empty :: Distances) distances where
decideMin :: Point -> Len -> ((Point, Len), Distances) -> ((Point, Len), Distances)
decideMin p' l' ((p, l), dist) = if findDistance graph p p' + l < l' then
((p, l), Map.insert p' (l + findDistance graph p p') dist) else
((p, l), Map.insert p' l' dist)
findDistance :: Graph -> Point -> Point -> Len
findDistance ((Node (a, b, len)) : graph) p p' = if ((a == p) && (b == p') || (b == p) && (a == p')) then len else findDistance graph p p'
findDistance _ _ _ = infinity
findMinNotVisited :: Vertices -> Distances -> Point
findMinNotVisited to_visit distances = fst $ Set.foldl (\(p, l) p' -> case Map.lookup p' distances of
(Just l') -> if l' < l then
(p', l')
else (p,l))
(-1, infinity) to_visit
infinityDistances :: Point -> Int -> Distances
infinityDistances point n = Map.fromList $ (point, 0) : [(x, infinity) | x <- [1..n], x /= point]
countVertices :: Graph -> (Vertices, Int)
countVertices graph = foldl f (Set.empty :: Vertices, 0) graph where
f :: (Vertices, Int) -> Node -> (Vertices, Int)
f (vertices, n) (Node (a, b, _)) =
let aIsNotMember = not $ Set.member a vertices
bIsNotMember = not $ Set.member b vertices in if (aIsNotMember && bIsNotMember) then (Set.insert a $ Set.insert b vertices, n + 2)
else if (aIsNotMember) then (Set.insert a vertices, n + 1)
else if (bIsNotMember) then (Set.insert b vertices, n + 1)
else (vertices, n)
findNotVisitedNeighbours :: Graph -> Vertices -> Point -> Vertices
findNotVisitedNeighbours graph visited point = foldl f (Set.empty :: Vertices) graph where
f :: Vertices -> Node -> Vertices
f vertices (Node (a, b, _)) = if ((a == point) && (not $ Set.member b visited)) then Set.insert b vertices
else if ((b == point) && (not $ Set.member a visited)) then Set.insert a vertices
else vertices

13
03_dijkstra/Types.hs Normal file
View file

@ -0,0 +1,13 @@
module Types where
import qualified Data.Map as Map
import qualified Data.Set as Set
type Point = Int
type Len = Int
data Node = Node (Point, Point, Len) deriving Show
type Graph = [Node]
type Distances = Map.Map Point Len
type Vertices = Set.Set Point

9
03_dijkstra/data Normal file
View file

@ -0,0 +1,9 @@
4
1 2 10
1 4 2
1 3 3
3 2 5
2 4 6
4 5 4
5 3 1

46
03_dijkstra/main.hs Normal file
View file

@ -0,0 +1,46 @@
module Main where
import System.IO
import System.Environment
import qualified Data.Map as Map
import qualified Data.Set as Set
import LeastReachableCity
import Types
{- format of input file is:
- mileage
- from to len
- from to len
- ...
- where "from" and "to" are numbers of graph vertices, "len" is distance between them
- -}
main :: IO()
main = do
argv <- getArgs
filename <- if not $ null argv then do
head <$> getArgs
else do
putStrLn "Usage: ./lrc [filename]\nNow defaulting to file \"data\""
return "data"
content <- readFile filename
let mileage = read $ head $ lines content :: Len
let graph = readGraph $ tail content
let (city, n) = leastReachableCity graph mileage
putStrLn $ "The least reachable city is " ++ (show city) ++ " (" ++ (show n) ++ " city/ies is/are reachable)"
{- For parsing graph using content of the file -}
parseStrNode :: [String] -> Node
parseStrNode [a, b, c] = Node (p a, p b, l c) where
p x = read x :: Point
l x = read x :: Len
readGraph :: String -> Graph
readGraph content = parse lst where
parse (x:xs) = if length ws == 3 then (parseStrNode ws) : (parse xs) else parse xs where
ws = words x
parse _ = []
lst = lines content