Facebook Pixel

3394. Check if Grid can be Cut into Sections

Problem Description

You are given an integer n representing the dimensions of an n x n grid, with the origin located at the bottom-left corner. Additionally, a 2D array rectangles is provided, where each element is in the form [start_x, start_y, end_x, end_y]. This array details the coordinates of non-overlapping rectangles on the grid:

  • (start_x, start_y): Represents the bottom-left corner of the rectangle.
  • (end_x, end_y): Represents the top-right corner.

The challenge is to determine if it is possible to make either two horizontal cuts or two vertical cuts on the grid such that:

  • Each of the three sections resulting from these cuts contains at least one rectangle.
  • Every rectangle belongs to exactly one section.

Your task is to return true if such cuts can be made, else return false.

Intuition

To solve this problem, the approach involves detecting potential cutting lines that can divide the grid into three sections, each containing at least one rectangle. The idea is to explore both horizontal and vertical potential cutting lines using the coordinates of the rectangles provided.

Here's the reasoning:

  1. Representation of Coordinates:

    • For each rectangle, define two intersections: one when the rectangle begins and one when it ends. This will help track the number of overlapping rectangles as we traverse through the grid.
  2. Sorting and Traversal:

    • Sort these events based on their coordinates, and for ties, prioritize 'end' events over 'start' events. This ensures that when counting overlaps, ending a rectangle does not prematurely affect the count of intersections.
  3. Counting Line Intersections:

    • As the sorted list of events is traversed, an overlap counter is maintained: incrementing on a 'start' and decrementing on an 'end'.
    • A potential cutting line is determined when the overlap count reaches zero, meaning the grid is clear at that line. We need at least three such clear lines to form three distinct sections.

By applying these steps both horizontally and vertically, if either direction provides partitioning with three valid sections, it means the grid can be cut accordingly. Therefore, the solution checks these conditions and returns whether such a partition exists.

Learn more about Sorting patterns.

Solution Approach

The solution involves an algorithm that efficiently checks if the provided rectangles can be divided with vertical or horizontal cuts such that each section contains at least one rectangle.

  1. Coordinate Representation:

    • For each rectangle in the list, create two events for both x-coordinates and y-coordinates:
      • A 'start' event when a rectangle begins at (start_x, start_y).
      • An 'end' event when a rectangle ends at (end_x, end_y).
    • These events are represented as tuples (coordinate, type) where type is 1 for 'start' and 0 for 'end'.
  2. Data Structures:

    • Use lists to store these coordinate events for both x and y directions separately.
  3. Sorting:

    • Sort both the x and y coordinate events. The sorting key ensures that on a tie in coordinate value, 'end' events come before 'start' events, using the sort key (coordinate, event type).
  4. Counting Line Intersections:

    • Implement the countLineIntersections method, which traverses the sorted coordinate list. It maintains an overlap counter:
      • Increment the counter for a 'start' event.
      • Decrement the counter for an 'end' event.
    • A clear line occurs when the overlap counter reaches zero.
    • Count the number of these clear lines where the overlap is zero.
  5. Validation:

    • The method proceeds by checking both the vertical (x_coordinates) and horizontal (y_coordinates) possibilities:
      • If either dimension allows for three clear lines, then it returns True.
    • Otherwise, return False.

The algorithm effectively leverages sorting and a linear traversal to achieve the desired solution, ensuring that it checks for partitionability in both vertical and horizontal directions.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach:

Suppose we have a grid of size 4 x 4 (n = 4) and the following list of rectangles:

rectangles = [
    [1, 0, 2, 2],  # Rectangle A
    [2, 0, 4, 1],  # Rectangle B
    [0, 2, 1, 4]   # Rectangle C
]

Step 1: Coordinate Representation

  • For each rectangle, create coordinate events for both x-coordinates and y-coordinates:
    • Rectangle A has x-events (1, 1) and (2, 0); y-events (0, 1) and (2, 0)
    • Rectangle B has x-events (2, 1) and (4, 0); y-events (0, 1) and (1, 0)
    • Rectangle C has x-events (0, 1) and (1, 0); y-events (2, 1) and (4, 0)

Step 2: Data Structures

  • x-events: [(1, 1), (2, 1), (4, 0), (0, 1), (2, 0), (1, 0)]
  • y-events: [(0, 1), (2, 0), (1, 0), (2, 1), (4, 0), (0, 1)]

Step 3: Sorting

  • Sort the coordinate events, prioritizing 'end' events before 'start' events when there is a tie:
    • Sorted x-events: [(0, 1), (1, 1), (1, 0), (2, 1), (2, 0), (4, 0)]
    • Sorted y-events: [(0, 1), (0, 1), (1, 0), (2, 0), (2, 1), (4, 0)]

Step 4: Counting Line Intersections

  • Count clear lines in x and y:
    • Traverse x-events, maintain an overlap counter:

      • Start overlapping at x=0: overlap = 1
      • x=1: overlap increases to 2, then decreases to 1
      • x=2: overlap increases to 2, then decreases to 1
      • x=4: overlap decreases to 0 (clear line found)
    • Three clear distinct sections are not evident in x direction.

    • Traverse y-events:

      • Start overlapping at y=0: overlap = 2
      • y=1: overlap decreases to 1
      • y=2: overlap decreases to 0 (clear line found), and overlap increases to 1
      • y=4: overlap decreases to 0 (another clear line)
    • Three distinct sections are also not evident in y direction.

Step 5: Validation

  • Since neither x nor y events form three distinct sections with clear lines, the grid cannot be properly partitioned with the given rectangles.
  • Therefore, the function would return False.

Solution Implementation

1from typing import List
2
3class Solution:
4    def countLineIntersections(self, coordinates: List[tuple[int, int]]) -> bool:
5        # Initialize variables to track the number of lines and segments
6        lines = 0
7        overlap = 0
8
9        # Iterate through each coordinate and marker
10        for value, marker in coordinates:
11            # If marker is 0, it indicates the end of a segment, decrease overlap
12            if marker == 0:
13                overlap -= 1
14            # If marker is 1, it indicates the start of a segment, increase overlap
15            else:
16                overlap += 1
17
18            # If overlap is zero, increment the number of lines
19            if overlap == 0:
20                lines += 1
21
22        # At least 3 lines indicate that cuts can be made
23        return lines >= 3
24
25    def checkValidCuts(self, n: int, rectangles: List[List[int]]) -> bool:
26        # Initialize lists to store y and x coordinates with start and end markers
27        y_coordinates = []
28        x_coordinates = []
29
30        # Extract coordinates from each rectangle
31        for rect in rectangles:
32            x1, y1, x2, y2 = rect
33            y_coordinates.append((y1, 1))  # Start of a vertical segment
34            y_coordinates.append((y2, 0))  # End of a vertical segment
35
36            x_coordinates.append((x1, 1))  # Start of a horizontal segment
37            x_coordinates.append((x2, 0))  # End of a horizontal segment
38
39        # Sort coordinates by value; in case of ties, end (0) before start (1)
40        y_coordinates.sort(key=lambda coord: (coord[0], coord[1]))
41        x_coordinates.sort(key=lambda coord: (coord[0], coord[1]))
42
43        # Check if there are at least 3 non-overlapping segments in either direction
44        return self.countLineIntersections(y_coordinates) or self.countLineIntersections(x_coordinates)
45
1import java.util.ArrayList;
2import java.util.Collections;
3import java.util.Comparator;
4import java.util.List;
5
6class Solution {
7    // Helper class to mimic C++ pair<int, int>
8    static class Pair {
9        int value; // Coordinate value
10        int type;  // Identifier for start(1) or end(0) of a line
11
12        Pair(int value, int type) {
13            this.value = value;
14            this.type = type;
15        }
16    }
17
18    // Method to determine if there are at least three separate line intersections
19    private boolean countLineIntersections(List<Pair> coordinates) {
20        int lines = 0; // Count of separate line segments
21        int overlap = 0; // Tracks overlapping segments
22
23        for (Pair coord : coordinates) {
24            if (coord.type == 0) {
25                overlap--; // Ending a line segment
26            } else {
27                overlap++; // Starting a new line segment
28            }
29
30            if (overlap == 0) {
31                lines++; // A completely non-overlapping line segment is found
32            }
33        }
34
35        return lines >= 3; // Return true if three or more separate segments exist
36    }
37
38    // Main method to check for valid cuts based on line segment intersections
39    public boolean checkValidCuts(int n, int[][] rectangles) {
40        List<Pair> yCoordinates = new ArrayList<>(); // Store y-coordinate segments
41        List<Pair> xCoordinates = new ArrayList<>(); // Store x-coordinate segments
42
43        for (int[] rectangle : rectangles) {
44            // Extracting start and end points for y and x coordinates
45            yCoordinates.add(new Pair(rectangle[1], 1)); // y1 start point
46            yCoordinates.add(new Pair(rectangle[3], 0)); // y2 end point
47
48            xCoordinates.add(new Pair(rectangle[0], 1)); // x1 start point
49            xCoordinates.add(new Pair(rectangle[2], 0)); // x2 end point
50        }
51
52        // Comparator to first sort by coordinate value, then by type (end before start)
53        Comparator<Pair> comparator = (a, b) -> {
54            if (a.value != b.value) {
55                return Integer.compare(a.value, b.value); // Sort by coordinate value
56            }
57            return Integer.compare(a.type, b.type); // If same value, sort by type
58        };
59
60        // Sort the coordinate lists
61        Collections.sort(yCoordinates, comparator);
62        Collections.sort(xCoordinates, comparator);
63
64        // Check if either x or y coordinate segments have sufficient valid cuts
65        return countLineIntersections(yCoordinates) || countLineIntersections(xCoordinates);
66    }
67}
68
1#include <vector>
2#include <algorithm>
3
4class Solution {
5#define pii std::pair<int, int>  // Define a shorthand for pair<int, int>
6
7    // Function to count if there are at least three intersecting lines
8    bool countLineIntersections(std::vector<pii>& coordinates) {
9        int lineCount = 0;  // Counter for the number of non-overlapping lines
10        int overlap = 0;    // Counter for overlap management
11
12        // Iterate through all coordinate pairs
13        for (int i = 0; i < coordinates.size(); ++i) {
14            // Adjust overlap based on whether it is a start (1) or end (0) of a line
15            if (coordinates[i].second == 0)
16                overlap--;
17            else
18                overlap++;
19
20            // When overlap is zero, we have found a complete set of lines
21            if (overlap == 0)
22                lineCount++;
23        }
24        return lineCount >= 3;  // Check if at least three complete lines exist
25    }
26
27public:
28    // Function to check if at least 3 vertical or horizontal cuts are possible
29    bool checkValidCuts(int n, std::vector<std::vector<int>>& rectangles) {
30        std::vector<pii> yCoordinates, xCoordinates;
31
32        // Process each rectangle
33        for (auto& rectangle : rectangles) {
34            // Collect y-coordinates (heights) with an indication of line start (1) and end (0)
35            yCoordinates.push_back(std::make_pair(rectangle[1], 1));  // Start of vertical line
36            yCoordinates.push_back(std::make_pair(rectangle[3], 0));  // End of vertical line
37
38            // Collect x-coordinates (widths) similarly
39            xCoordinates.push_back(std::make_pair(rectangle[0], 1));  // Start of horizontal line
40            xCoordinates.push_back(std::make_pair(rectangle[2], 0));  // End of horizontal line
41        }
42
43        // Sort the coordinate vectors for the line-sweep algorithm
44        std::sort(yCoordinates.begin(), yCoordinates.end());
45        std::sort(xCoordinates.begin(), xCoordinates.end());
46
47        // Apply line-sweep technique on both x and y coordinates to check for valid cuts
48        return (countLineIntersections(yCoordinates) || countLineIntersections(xCoordinates));
49    }
50};
51
1// Function to determine if there are valid cuts in a set of rectangles such that
2// no more than three cuts intersect all rectangles.
3function checkValidCuts(n: number, rectangles: number[][]): boolean {
4
5    // Helper function to check if three non-overlapping segments can cover all given segments
6    const check = (arr: number[][], getVals: (x: number[]) => number[]): boolean => {
7        let cutsRemaining = 3; // Maximum allowed non-overlapping segments
8        let longestEnd = 0;    // End of the current longest segment
9
10        for (const rect of arr) {
11            const [start, end] = getVals(rect);
12
13            // If the start of the current segment is less than the longest end extended so far
14            if (start < longestEnd) {
15                longestEnd = Math.max(longestEnd, end); // Extend the longest end
16            } else {
17                longestEnd = end; // Update the longest end to current end
18                if (--cutsRemaining === 0) return true; // Use one cut and check if no cuts left
19            }
20        }
21        return false; // Return false if unable to cover all segments using three cuts
22    };
23
24    // Comparator function to sort rectangles by x-coordinate
25    const sortByX = ([a]: number[], [b]: number[]): number => a - b;
26
27    // Comparator function to sort rectangles by y-coordinate
28    const sortByY = ([, a]: number[], [, b]: number[]): number => a - b;
29
30    // Extract x-coordinates (start and end) from a rectangle
31    const getX = ([x1, , x2]: number[]): number[] => [x1, x2];
32
33    // Extract y-coordinates (start and end) from a rectangle
34    const getY = ([, y1, , y2]: number[]): number[] => [y1, y2];
35
36    // Check for valid cuts either on x-coordinates or y-coordinates
37    return check(rectangles.toSorted(sortByX), getX) || check(rectangles.toSorted(sortByY), getY);
38}
39

Time and Space Complexity

The code iterates through the list of rectangles and performs operations on each of them. Let's analyze the complexities:

Time Complexity

  1. Iterating Over Rectangles: The loop at for rect in rectangles: iterates over n rectangles, resulting in an O(n) complexity for this part.
  2. Appending Coordinates: Appending coordinates of start and end points for each rectangle involves adding two elements for y-coordinates and two for x-coordinates, maintaining the O(n) complexity.
  3. Sorting Operations: Both y_coordinates and x_coordinates lists are sorted, each containing 2n points. Sorting these lists has a complexity of O(n log n).
  4. Count Line Intersections: The countLineIntersections method processes each sorted list once, iterating over 2n elements per list. This yields a linear complexity of O(2n) which is simplified to O(n) as constants are negligible.

Thus, the overall time complexity of the solution is O(n log n) due to the sorting step.

Space Complexity

  1. Coordinate Storage: Two lists y_coordinates and x_coordinates are used, each storing 2n elements, leading to a space complexity of O(n) for each list.
  2. Additional Variables: Other variables occupy constant space, which does not affect overall complexity significantly.

Hence, the overall space complexity is O(n), dominated by the lists storing the coordinates.

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More