-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathP84.hs
More file actions
84 lines (74 loc) · 3.05 KB
/
P84.hs
File metadata and controls
84 lines (74 loc) · 3.05 KB
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
{-# LANGUAGE LambdaCase #-}
module Graph.P84 where
import qualified Control.Monad.State as S
import qualified Data.HashPSQ as Q
import Data.Hashable (Hashable)
import qualified Data.Set as Set
{-
Problem 84: (**) Construct the minimal spanning trees.
ANSWER: We use Prim's eager MST algorithm.
https://www.youtube.com/watch?v=xq3ABa-px_g
- Maintain a min Indexed Priority Queue (IPQ) of size V
that sorts vertex-edge pairs based on the min edge cost
of e. By default, all vertices v have a best value of ∞
in the IPQ.
- Start the algorithm on any node 's'. Mark s as visited
and relax all edges of s.
Relaxing refers to updating the entry for node v in the
IPQ from (v, old_edge) to (v, new_edge) if the new_edge
from u -> v has a lower cost than old_edge.
- While the IPQ is not empty and a MST has not been formed,
deque the next best (v, e) pair from the IPQ. Mark node v
as visited and add edge e to the MST.
- Next, relax all edges of v while making sure not to relax
any edge pointing to a node which has already been visited.
This algorithm runs in O(E log V) time since there can only
be V (node, edge) pairs in the IPQ, making the update and
poll operations O(log V).
-}
prim :: (Ord a, Hashable a) => [(a, a, Int)] -> [(a, a, Int)]
prim edges = S.evalState go initialState
where
(u0, _, _) = head edges
-- Start with all edges incident to u0 on the heap.
initialState = (Set.singleton u0, relax Q.empty (outE u0))
-- Sorts the given edge so that vertex u appears first.
sortE u (x, y, cost) = if x == u then (x, y, cost) else (y, x, cost)
-- Determines if the given edge is incident to u.
isIncidentTo u (x, y, _) = x == u || y == u
-- Finds all edges incident to u, and sorts them so that u appears first.
outE u = (map (sortE u) . filter (isIncidentTo u)) edges
-- Relaxes the given edges.
relax = foldl update
-- If the edge (from-to) is not present on the heap, inserts it,
-- otherwise if the edge on the heap has a greater cost,
-- replaces it with the given edge.
update q (from, to, cost) =
snd $
Q.alter
( \case
Nothing -> ((), Just (cost, from))
Just (p, v) ->
if p <= cost
then ((), Just (p, v))
else ((), Just (cost, from))
)
to
q
go = do
(visited, q) <- S.get
-- Edges are put on the queue as v: (u, priority),
-- where the edge is incident to the vertex 'v'
-- from the vertex 'u'. The edge cost is the priority.
case Q.minView q of
Nothing -> return []
-- At each iteration, pick an edge (v, u) with the minimum cost,
-- and relax all edges incident to v, except for those connected
-- to vertices already visited.
Just (to, cost, from, rest) -> do
let out = filter (\(_, v, _) -> v `Set.notMember` visited) $ outE to
let q' = relax rest out
let visited' = Set.insert to visited
S.put (visited', q')
xs <- go
return $ (from, to, cost) : xs