Last active
June 16, 2016 23:20
-
-
Save tippenein/15e4f50ae5738a7da0c45986d18ada59 to your computer and use it in GitHub Desktop.
convex hull perimeter - https://www.hackerrank.com/challenges/convex-hull-fp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Text.Printf | |
import Data.List | |
data P = P (Double, Double) deriving (Ord, Show, Eq) | |
class Geom a where | |
sub :: a -> a -> a | |
crossProduct :: a -> a -> Double | |
distance :: a -> a -> Double | |
instance Geom P where | |
P (x1, y1) `sub` P (x2, y2) = P (x1 - x2, y1 - y2) | |
P (x1, y1) `crossProduct` P (x2, y2) = x1 * y2 - x2 * y1 | |
distance (P p1) (P p2) = abs $ sqrt (x'**2 + y'**2) | |
where | |
x' = fst p1 - fst p2 | |
y' = snd p1 - snd p2 | |
solve :: [P] -> Double | |
solve = perimeter . convexHull | |
convexHull :: [P] -> [P] | |
convexHull [] = [] | |
convexHull [p] = [p] | |
convexHull points = lower ++ upper | |
where | |
sorted = sort points | |
lower = chain sorted | |
upper = chain (reverse sorted) | |
chain :: [P] -> [P] | |
chain points = go [] points | |
where | |
go :: [P] -> [P] -> [P] | |
go acc@(p1:p2:ps) (x:xs) = | |
if clockwise p2 p1 x | |
-- remove the most recent part of the chain. | |
then go (p2:ps) (x:xs) | |
-- append to the chain. | |
else go (x:acc) xs | |
-- If one point in the chain, just add the next visited point. | |
go acc (x:xs) = go (x:acc) xs | |
-- skip the duplicated point | |
go acc [] = tail acc | |
clockwise :: P -> P -> P -> Bool | |
clockwise origin a b = (a `sub` origin) `crossProduct` (b `sub` origin) <= 0 | |
perimeter :: [P] -> Double | |
perimeter [] = error "empty point list" | |
perimeter (a:b:[]) = error "not enough points to make a perimeter" | |
perimeter ch = fst $ foldl' addUp (0, firstPoint) appended | |
where | |
appended = ch ++ [firstPoint] -- complete the perimeter | |
firstPoint = head ch | |
addUp totalTuple p2 = (,) (fst totalTuple + distance (snd totalTuple) p2) p2 | |
main :: IO () | |
main = do | |
n <- readLn :: IO Int | |
content <- getContents | |
let | |
points = map (\[x, y] -> P (x, y)). map (map (read::String->Double)). map words. lines $ content | |
ans = solve points | |
printf "%.1f\n" ans |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment