Compare commits
	
		
			2 commits
		
	
	
		
			c7f29e2154
			...
			a31509d093
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a31509d093 | ||
|  | 74f5dbb620 | 
					 4 changed files with 143 additions and 0 deletions
				
			
		
							
								
								
									
										75
									
								
								03_dijkstra/LeastReachableCity.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								03_dijkstra/LeastReachableCity.hs
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										13
									
								
								03_dijkstra/Types.hs
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										9
									
								
								03_dijkstra/data
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										46
									
								
								03_dijkstra/main.hs
									
										
									
									
									
										Normal 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 | ||||||
		Reference in a new issue