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:
-
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.
-
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.
-
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.
-
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)
.
- A 'start' event when a rectangle begins at
- These events are represented as tuples
(coordinate, type)
wheretype
is1
for 'start' and0
for 'end'.
- For each rectangle in the list, create two events for both x-coordinates and y-coordinates:
-
Data Structures:
- Use lists to store these coordinate events for both x and y directions separately.
-
- 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)
.
- 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
-
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.
- Implement the
-
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
.
- If either dimension allows for three clear lines, then it returns
- Otherwise, return
False
.
- The method proceeds by checking both the vertical (
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 EvaluatorExample 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)
- Rectangle A has x-events
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)]
- Sorted x-events:
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 1x=2
: overlap increases to 2, then decreases to 1x=4
: overlap decreases to 0 (clear line found)
- Start overlapping at
-
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 1y=2
: overlap decreases to 0 (clear line found), and overlap increases to 1y=4
: overlap decreases to 0 (another clear line)
- Start overlapping at
-
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
- Iterating Over Rectangles: The loop at
for rect in rectangles:
iterates overn
rectangles, resulting in anO(n)
complexity for this part. - 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. - Sorting Operations: Both
y_coordinates
andx_coordinates
lists are sorted, each containing2n
points. Sorting these lists has a complexity ofO(n log n)
. - Count Line Intersections: The
countLineIntersections
method processes each sorted list once, iterating over2n
elements per list. This yields a linear complexity ofO(2n)
which is simplified toO(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
- Coordinate Storage: Two lists
y_coordinates
andx_coordinates
are used, each storing2n
elements, leading to a space complexity ofO(n)
for each list. - 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.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!