This day’s problem is based on simple coordinate geometry.
Parse the input data first (pairs of numbers separated by commas, one pair per line):
import Data.List.Split (splitOn)
type Point = [Integer]
parse :: String -> [Point]
parse = map (map read . splitOn ",") . linesOnto the problem itself.
This part requires us to simply find a rectangle with the
largest area among the points given to us, so first write a method
to compute areas given a point. Remember that our representation
of a point is [x, y]:
area :: Point -> Point -> Integer
area [x, y] [x', y'] =
(1 + abs (x - x')) * (1 + abs (y - y'))Thus, the solution to the first part is given by the following list comprehension:
p1 :: [Point] -> Integer
p1 poly = maximum [area p p' | p <- poly, p' <- poly]This part is a whole lot trickier. We now require only rectangles that fall within the polygon. Rectangles that have areas outside the polygon should be ignored. The methodology here is to identify rectangles that do not intersect in any way with the polygon itself. Sharing an edge with the polygon is alright however.
Luckily, the intersections are quite easily calculated, since all lines of the polygon are oriented along the axes, and so are the lines forming the rectangle. If a line of the polygon falls to the left of the leftmost line of the rectangle, or to the right of the rightmost line, or above the topmost line, or below the bottommost line, then the line does not intersect the rectangle:
intersects :: Point -> Point -> [Point] -> Bool
intersects [x, y] [x', y'] = not . all away . pairs
where
pairs (p : ps) = zip (p : ps) (ps ++ [p])
away ([lx, ly], [lx', ly']) =
(max lx lx' <= min x x')
|| (min lx lx' >= max x x')
|| (max ly ly' <= min y y')
|| (min ly ly' >= max y y')To explain a bit further, we define away according
the the rules above. Then the intersects method is
given by simply ensuring that of all pairs of points in the
polygon (we need pairs to compute line segments of the polygon),
we have atleast one line segment that is not away from the
rectangle.
Thus, the solution to the second part is a similar list comprehension with an additional guard:
p2 :: [Point] -> Integer
p2 poly =
maximum
[ area p p'
| p <- poly,
p' <- poly,
not (intersects p p' poly)
]Finally, a main function to wrap it all up:
main = do
n <- parse <$> getContents
print $ p1 n
print $ p2 n