587. Erect the Fence

587. Erect the Fence

Problem Solving - Day 49

ยท

5 min read

Hello, reader ๐Ÿ‘‹๐Ÿฝ ! Welcome to day 49 of the series on Problem Solving. Through this series, I aim to pick up at least one question everyday and share my approach for solving it.

Today, I will be picking up LeetCode's daily challenge problem: 587. Erect the Fence.


๐Ÿค” Problem Statement - Monotone Chain

  • There is an array trees where trees[i] = [xi, yi] represents the location of a tree in the garden.
  • The garden has to be fenced using the minimum length of rope as it is expensive. The garden is well fenced only if all the trees are enclosed.
  • Return the coordinates of trees that are exactly located on the fence perimeter.

  • E.g.:

    • points = [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]] => [[1,1],[2,0],[3,3],[2,4],[4,2]]

๐Ÿ’ฌ Thought Process - Montone Chain

  • Honestly, I didn't solve this question on my own at all. Because I knew right away that it involves fancy master's level math that's I am not aware of.
  • The idea of the approach is to sort all the tree coordinates by their x indices. If the x indices are same, then they are sorted by their y indices. This is because we are trying to find the farthest left and right points.
  • Coordinates of the tree forms a convex hull which is divided into two parts: the upper and lower hull.
  • The fence coordinates for both the hulls can be found separately.

    For your reference, given a set of points in a 2D plane, convex hull is the smallest polygon that contains all points within it.

  • Before calculating the fence coordinates, if the array only contains one tree coordinate, then we simply return the input.

  • It is also worth noting that in a convex hull the adjacent point's edges have a degree less than 180.
  • Hence we will try figuring out the angles between the edges of points using the formula to find the slope between two points.
  • To do this, we can use basic algebra to find the angle between adjacent edges formed by points. image.png

  • Noice how we require three points to figure out angles between adjacent edges.

  • Also in the image m23 > m12. So, sort the list of points to ensure that we find the angles between edges are increasing. We try to find angles that go in the clock wise direction.
  • But this could not be the case always. There could be a case when the slope of [x1,y1] and [x2,y2] could be greater than that between [x3,y3] and [x2,y2] and hence the angle becomes > 180. anti-clockwise lope.png
  • To handle both the cases we will break the convex into two parts as already stated.
  • The coordinates in the upper half of the hull have angles between edges that go in the anti-clockwise direction. And the coordinates in the lower half of the hull have angles that go in the clockwise direction.
  • We will calculate the points separately such that all the points whose edges form angles that are no longer than 180 degree.
  • If any edge in the upper hull, the angle forms in a clockwise direction, it is ignored. Similarly for any edge in the lower hull forms an angle in the anti-clockwise direction, it is ignored.
  • For the lower hull, we add the first two points to a stack. Then we iterate over the remaining points checking if it forms an angle in the anti-clockwise direction with the previous two points. If it does form, we consider it for the fence and push it onto the stack. If not, we ignore it.
  • For the upper hull instead of finding angles in the clockwise direction, we start looking in the reverse order. Hence the anti-clockwise angles becomes angles in clockwise direction and vice versa.
  • Hence the logic remains same and we will look for points again in anti-clockwise direction.
  • Finally we iterate over all the valid points and remove the duplicates. Then we return the points.

๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ป Solution - Monotone Chain

  • Below is the code for the above discussed approach. ``` class Point { int x, y; Point(int x, int y) {
      this.x = x;
      this.y = y;
    
    } }

class Solution { public int[][] outerTrees(int[][] trees) { if(trees.length == 1) return trees;

    List<Point> points = new ArrayList<>();

    for(int[] tree: trees) {
        Point p = new Point(tree[0], tree[1]);
        points.add(p);
    }
    int n = points.size();

    Collections.sort(points, (a,b) -> a.x == b.x ? a.y - b.y : a.x - b.x);
    Set<List<Integer>> duplicates = new HashSet();

    Stack<Point> hull = new Stack();
    hull.push(points.get(0));
    hull.push(points.get(1));

    for(int i = 2; i<n; i++) {
        Point top = hull.pop();
        while(!hull.isEmpty() && checkAngles(hull.peek(), top, points.get(i)) < 0) {
            top = hull.pop();
        }
        hull.push(top);
        hull.push(points.get(i));
    }

    for(int i = n-2; i>=0; i--) {
        Point top = hull.pop();
        while(!hull.isEmpty() && checkAngles(hull.peek(), top, points.get(i)) < 0) {
            top = hull.pop();
        }
        hull.push(top);
        hull.push(points.get(i));
    }

    List<Point> answer = new ArrayList();
    int index = 0;
    for(Point p: hull) {
        ArrayList<Integer> temp = new ArrayList();
        temp.add(p.x);
        temp.add(p.y);

        if(duplicates.contains(temp)) continue;
        duplicates.add(temp);
        answer.add(p);
    }

    int[][] ans = new int[answer.size()][2];
    int i = 0;

    for(Point p: answer) {
        ans[i][0] = p.x;
        ans[i][1] = p.y;
        i++;
    }

    return ans;
}

private int checkAngles(Point p, Point q, Point r) {
    return (q.x - p.x) * (r.y - p.y) - (q.y - p.y)* (r.x - p.x);
}

}


Time Complexity: O(n log n)

- n = number of coorindates

Space Complexity: O(n)

- n = number of coordinates

```


You can find the link to the GitHub repo for this question here: 587. Erect the Fence.


Conclusion

That's a wrap for today's problem. If you liked my explanation then please do drop a like/ comment. Also, please correct me if I've made any mistakes or if you want me to improve something!

Thank you for reading!