Skip to content

Instantly share code, notes, and snippets.

@tippenein
Last active June 16, 2016 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tippenein/15e4f50ae5738a7da0c45986d18ada59 to your computer and use it in GitHub Desktop.
Save tippenein/15e4f50ae5738a7da0c45986d18ada59 to your computer and use it in GitHub Desktop.
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